はじめに なぜ「ファイルコピー」が業務で重要なのか
業務システムでは、バックアップを取る、別フォルダにエクスポートする、他システム連携用の受け渡しフォルダにコピーする、といった「ファイルを別の場所に複製する」処理が頻繁に登場します。
手作業でコピーしているうちはよくても、処理件数が増えたり、夜間バッチで自動化したりすると、「確実に・安全に・同じルールでコピーする」ことがとても大事になります。
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.csv を C:\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#overwrite を true にすると、コピー先に同名ファイルがあっても上書きされます。false の場合は、コピー先に既にファイルがあると IOException が発生します。
業務では、「バックアップは毎回上書きでよいのか」「日付付きで別名保存すべきか」など、運用ルールに合わせてこの挙動を決めることが重要です。
実務でよく使うパターンをユーティリティ化する
「存在チェック+コピー」をひとまとめにする
業務コードでは、「コピー元が存在するか」「コピー先に既にファイルがあるか」を確認してからコピーしたい場面が多いです。
そのたびに File.Exists と File.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.GetFileNameWithoutExtension と Path.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 系メソッドでファイル名や拡張子を扱うこと。
例外やエラーの原因をある程度分類し、ログやメッセージで「何が起きたのか」を分かるようにしておくこと。
ここまで押さえておけば、「バックアップが取れていなかった」「コピーに失敗していたのに誰も気づかなかった」といった、現場でよくあるトラブルをかなり減らせます。
