C# Tips | ファイル・ディレクトリ操作:ファイルコピー

C sharp C#
スポンサーリンク

はじめに なぜ「ファイルコピー」が業務で重要なのか

業務システムでは、バックアップを取る、別フォルダにエクスポートする、他システム連携用の受け渡しフォルダにコピーする、といった「ファイルを別の場所に複製する」処理が頻繁に登場します。
手作業でコピーしているうちはよくても、処理件数が増えたり、夜間バッチで自動化したりすると、「確実に・安全に・同じルールでコピーする」ことがとても大事になります。

C# では System.IO.File.Copy を使うことで、比較的シンプルなコードでファイルコピーを実装できます。
ここでは、プログラミング初心者でも理解できるように、基本から実務で使えるユーティリティ化まで、段階的に解説していきます。


基本のメソッド File.Copy を理解する

File.Copy の最もシンプルな使い方

ファイルコピーの基本は File.Copy です。
まずは一番シンプルな形から見てみましょう。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\data\sales.csv";          // コピー元
        string destPath   = @"C:\backup\sales.csv";        // コピー先

        File.Copy(sourcePath, destPath);

        Console.WriteLine("ファイルをコピーしました。");
    }
}
C#

このコードは、「C:\data\sales.csvC:\backup\sales.csv にコピーする」という意味です。
ここで重要なのは、コピー先のファイルが既に存在している場合、File.Copy は例外を投げて失敗するという点です。
つまり、「上書きするかどうか」を意識せずに書くと、思わぬところで例外が発生します。

上書きするかどうかを指定する

File.Copy には、第三引数に bool を渡せるオーバーロードがあります。
これを使うと、「コピー先に同名ファイルがあるときに上書きするかどうか」を指定できます。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\data\sales.csv";
        string destPath   = @"C:\backup\sales.csv";

        bool overwrite = true;  // true: 上書きする, false: 上書きしない

        File.Copy(sourcePath, destPath, overwrite);

        Console.WriteLine("ファイルコピーが完了しました。");
    }
}
C#

overwritetrue にすると、コピー先に同名ファイルがあっても上書きされます。
false の場合は、コピー先に既にファイルがあると IOException が発生します。
業務では、「バックアップは毎回上書きでよいのか」「日付付きで別名保存すべきか」など、運用ルールに合わせてこの挙動を決めることが重要です。


実務でよく使うパターンをユーティリティ化する

「存在チェック+コピー」をひとまとめにする

業務コードでは、「コピー元が存在するか」「コピー先に既にファイルがあるか」を確認してからコピーしたい場面が多いです。
そのたびに File.ExistsFile.Copy を毎回書くと、コードが散らばって読みにくくなります。

そこで、よく使うパターンをユーティリティメソッドにまとめておくと便利です。

using System;
using System.IO;

public static class FileCopyUtil
{
    public static void CopyWithCheck(string sourcePath, string destPath, bool overwrite)
    {
        if (!File.Exists(sourcePath))
        {
            throw new FileNotFoundException($"コピー元ファイルが見つかりません: {sourcePath}", sourcePath);
        }

        if (!overwrite && File.Exists(destPath))
        {
            throw new IOException($"コピー先に同名ファイルが既に存在します: {destPath}");
        }

        File.Copy(sourcePath, destPath, overwrite);
        Console.WriteLine($"ファイルコピー完了: {sourcePath} -> {destPath}");
    }
}
C#

このユーティリティを使う側は、次のように書けます。

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\data\sales.csv";
        string destPath   = @"C:\backup\sales.csv";

        try
        {
            FileCopyUtil.CopyWithCheck(sourcePath, destPath, overwrite: true);
            Console.WriteLine("業務処理としてのバックアップコピーが正常に完了しました。");
        }
        catch (Exception ex)
        {
            Console.WriteLine("ファイルコピー中にエラーが発生しました: " + ex.Message);
            // 実務ではここでログ出力や通知などを行う
        }
    }
}
C#

ここでの重要ポイントは、「コピー元の存在チェック」「コピー先の上書き可否チェック」「実際のコピー処理」を一箇所に集約していることです。
仕様変更(たとえば「上書き禁止にしたい」「ログの出し方を変えたい」など)があっても、ユーティリティだけを修正すれば済むようになります。


ディレクトリと組み合わせた実務的なコピー

コピー先ディレクトリがなければ作る

実務では、「コピー先のフォルダがまだ存在しない」という状況もよくあります。
その場合、ディレクトリを事前に作成しておかないと、File.Copy は「パスが見つかりません」といった例外を投げて失敗します。

コピー先ディレクトリを自動で作成するユーティリティの例を見てみましょう。

using System;
using System.IO;

public static class FileCopyUtil
{
    public static void CopyEnsureDirectory(string sourcePath, string destPath, bool overwrite)
    {
        if (!File.Exists(sourcePath))
        {
            throw new FileNotFoundException($"コピー元ファイルが見つかりません: {sourcePath}", sourcePath);
        }

        string? destDirectory = Path.GetDirectoryName(destPath);

        if (!string.IsNullOrEmpty(destDirectory) && !Directory.Exists(destDirectory))
        {
            Directory.CreateDirectory(destDirectory);
            Console.WriteLine($"コピー先ディレクトリを作成しました: {destDirectory}");
        }

        File.Copy(sourcePath, destPath, overwrite);
        Console.WriteLine($"ファイルコピー完了: {sourcePath} -> {destPath}");
    }
}
C#

使い方の例は次の通りです。

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\data\sales.csv";
        string destPath   = @"C:\backup\2025\01\sales.csv";

        FileCopyUtil.CopyEnsureDirectory(sourcePath, destPath, overwrite: true);
    }
}
C#

ここで深掘りしておきたいのは、Path.GetDirectoryName(destPath) を使って「コピー先パスからディレクトリ部分だけを取り出している」点です。
これにより、「コピー先のフォルダ構成がまだ存在しない場合に、自動で階層ごと作成する」という実務的な動きが実現できます。


バックアップや履歴保存でよく使う「別名コピー」

日付付きファイル名でコピーする

業務では、「毎回上書きするのではなく、日付や時刻を付けて履歴として残したい」という要件もよくあります。
その場合は、コピー先のファイル名を動的に組み立てるのが定番です。

using System;
using System.IO;

public static class FileBackupUtil
{
    public static string BackupWithTimestamp(string sourcePath, string backupFolder)
    {
        if (!File.Exists(sourcePath))
        {
            throw new FileNotFoundException($"バックアップ元ファイルが見つかりません: {sourcePath}", sourcePath);
        }

        Directory.CreateDirectory(backupFolder);

        string fileNameWithoutExt = Path.GetFileNameWithoutExtension(sourcePath);
        string ext = Path.GetExtension(sourcePath);

        string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        string backupFileName = $"{fileNameWithoutExt}_{timestamp}{ext}";

        string destPath = Path.Combine(backupFolder, backupFileName);

        File.Copy(sourcePath, destPath, overwrite: false);

        Console.WriteLine($"バックアップ作成: {destPath}");
        return destPath;
    }
}
C#

使い方の例は次の通りです。

class Program
{
    static void Main()
    {
        string sourcePath   = @"C:\data\sales.csv";
        string backupFolder = @"C:\backup\sales";

        string backupPath = FileBackupUtil.BackupWithTimestamp(sourcePath, backupFolder);

        Console.WriteLine("バックアップファイルパス: " + backupPath);
    }
}
C#

ここでの重要ポイントは、Path.GetFileNameWithoutExtensionPath.GetExtension を使って、元ファイル名を分解していることです。
これにより、「元のファイル名+タイムスタンプ+元と同じ拡張子」という、業務でよく見るバックアップ命名ルールを簡単に実現できます。


例外とエラー処理を意識したコピー

どんな例外が起こり得るか

ファイルコピーでは、さまざまな理由で例外が発生する可能性があります。
たとえば、コピー元が存在しない、コピー先のフォルダが存在しない、権限がない、ディスク容量が足りない、コピー先に同名ファイルがあって上書き禁止になっている、などです。

初心者のうちは「とりあえず try-catch (Exception) で全部受ける」でも構いませんが、業務ではある程度原因を区別できるようにしておくと、運用が楽になります。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\data\sales.csv";
        string destPath   = @"C:\backup\sales.csv";

        try
        {
            File.Copy(sourcePath, destPath, overwrite: false);
            Console.WriteLine("ファイルコピーに成功しました。");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("コピー元ファイルが見つかりません: " + ex.Message);
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine("権限エラーが発生しました: " + ex.Message);
        }
        catch (IOException ex)
        {
            Console.WriteLine("入出力エラーが発生しました: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("想定外のエラーが発生しました: " + ex.Message);
        }
    }
}
C#

ここで深掘りしておきたいのは、「例外の種類ごとにメッセージや対応を変えられる」という点です。
たとえば、権限エラーなら「サーバー管理者に権限設定を依頼する」、ディスク容量不足なら「古いバックアップを削除する」といった運用アクションにつなげやすくなります。


まとめ 実務で使えるファイルコピーの考え方

ファイルコピーは、一見シンプルですが、業務システムではかなり重要な役割を持つ処理です。
だからこそ、「ただ動けばいいコピー」ではなく、「運用やトラブル対応まで見据えたコピー」を意識して設計することが大切です。

File.Copy の基本的な挙動(コピー先に同名ファイルがあるときの動き、overwrite 引数の意味)を正しく理解すること。
コピー元の存在チェック、コピー先ディレクトリの存在確認・自動作成をユーティリティ化して、同じパターンを安全に再利用できるようにすること。
バックアップや履歴保存では、日付や時刻を付けた別名コピーを行い、Path 系メソッドでファイル名や拡張子を扱うこと。
例外やエラーの原因をある程度分類し、ログやメッセージで「何が起きたのか」を分かるようにしておくこと。

ここまで押さえておけば、「バックアップが取れていなかった」「コピーに失敗していたのに誰も気づかなかった」といった、現場でよくあるトラブルをかなり減らせます。

タイトルとURLをコピーしました