C# Tips | ファイル・ディレクトリ操作:ファイル移動

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

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

業務システムでは、「処理前フォルダから処理済みフォルダへ移動する」「受信フォルダからアーカイブフォルダへ退避する」「一時フォルダから本番フォルダへ本番反映する」といった、「ファイルを別の場所へ動かす」処理が頻繁に登場します。
コピーと違い、「元を残さない」という性質があるため、設計を間違えると「ファイルが消えた」「どこに行ったか分からない」という事故につながりやすいのがファイル移動です。

C# では System.IO.File.Move を使うことで、比較的シンプルにファイル移動を実装できます。
ここでは、プログラミング初心者向けに、基本から実務で使えるユーティリティ化まで、丁寧にかみ砕いて解説していきます。


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

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

ファイル移動の基本は File.Move です。
まずは一番シンプルな例から見てみましょう。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string sourcePath = @"C:\inbox\sales.csv";      // 移動元
        string destPath   = @"C:\processed\sales.csv";  // 移動先

        File.Move(sourcePath, destPath);

        Console.WriteLine("ファイルを移動しました。");
    }
}
C#

このコードは、「C:\inbox\sales.csvC:\processed\sales.csv に移動する」という意味です。
ここで重要なのは、移動先に同名ファイルが既に存在していると、IOException が発生して失敗するという点です。
コピーのときにあったような「上書きするかどうかの bool 引数」は、File.Move にはありません。

つまり、「移動先に同名ファイルが存在しないこと」を前提にするか、「存在していたらどうするか」を自分で設計する必要があります。

移動とコピーの違いを整理する

初心者が混乱しやすいポイントなので、「コピー」と「移動」の違いを言葉で整理しておきます。
コピーは「元を残したまま、同じ内容のファイルを別の場所に作る」操作です。
移動は「元の場所からファイルを取り除き、別の場所に置き直す」操作です。

業務的には、コピーは「バックアップ」「複製」「配布」に使われることが多く、
移動は「状態の変化(未処理→処理済み)」「整理」「アーカイブ」に使われることが多いです。
この違いを意識しておくと、「ここはコピーか、移動か」の判断がしやすくなります。


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

「存在チェック+移動」をひとまとめにする

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

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

using System;
using System.IO;

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

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

            File.Delete(destPath);
        }

        File.Move(sourcePath, destPath);
        Console.WriteLine($"ファイル移動完了: {sourcePath} -> {destPath}");
    }
}
C#

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

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

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

ここで深掘りしたいポイントは、「上書きしたい場合は、自分で先に削除してから Move している」という点です。
File.Move 自体には「上書き」のオプションがないため、「上書きしたい」という要件があるなら、
「存在していたら削除する」というロジックを自分で書く必要があります。


ディレクトリと組み合わせた実務的な移動

移動先ディレクトリがなければ作る

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

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

using System;
using System.IO;

public static class FileMoveUtil
{
    public static void MoveEnsureDirectory(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}");
        }

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

            File.Delete(destPath);
        }

        File.Move(sourcePath, destPath);
        Console.WriteLine($"ファイル移動完了: {sourcePath} -> {destPath}");
    }
}
C#

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

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

        FileMoveUtil.MoveEnsureDirectory(sourcePath, destPath, overwrite: false);
    }
}
C#

ここで重要なのは、Path.GetDirectoryName(destPath) で「移動先パスからディレクトリ部分だけを取り出し」、
存在しなければ Directory.CreateDirectory で階層ごと作成している点です。
これにより、「年月ごとのアーカイブフォルダに自動で仕分ける」といった実務的な運用が実現しやすくなります。


状態管理としての「移動」 未処理→処理中→処理済み

フォルダで状態を表現する設計

業務バッチや連携処理では、「ファイルの状態」をフォルダで表現する設計がよく使われます。
たとえば、次のようなイメージです。

未処理フォルダ: C:\jobs\in
処理中フォルダ: C:\jobs\working
処理済みフォルダ: C:\jobs\done
エラーフォルダ: C:\jobs\error

このとき、ファイル移動は「状態遷移」を表す重要な操作になります。
未処理から処理中へ移動したら「今このファイルを処理している」、処理が成功したら処理済みへ移動、失敗したらエラーフォルダへ移動、という具合です。

状態遷移をユーティリティで表現する

状態遷移を分かりやすくするために、「状態を意識した移動ユーティリティ」を用意するのも実務的です。

using System;
using System.IO;

public static class JobFileMover
{
    public static void MoveToWorking(string filePath, string workingFolder)
    {
        Directory.CreateDirectory(workingFolder);

        string fileName = Path.GetFileName(filePath);
        string destPath = Path.Combine(workingFolder, fileName);

        FileMoveUtil.MoveEnsureDirectory(filePath, destPath, overwrite: false);
    }

    public static void MoveToDone(string filePath, string doneFolder)
    {
        Directory.CreateDirectory(doneFolder);

        string fileName = Path.GetFileName(filePath);
        string destPath = Path.Combine(doneFolder, fileName);

        FileMoveUtil.MoveEnsureDirectory(filePath, destPath, overwrite: true);
    }

    public static void MoveToError(string filePath, string errorFolder)
    {
        Directory.CreateDirectory(errorFolder);

        string fileName = Path.GetFileName(filePath);
        string destPath = Path.Combine(errorFolder, fileName);

        FileMoveUtil.MoveEnsureDirectory(filePath, destPath, overwrite: true);
    }
}
C#

使い方のイメージは次の通りです。

class Program
{
    static void Main()
    {
        string inFolder      = @"C:\jobs\in";
        string workingFolder = @"C:\jobs\working";
        string doneFolder    = @"C:\jobs\done";
        string errorFolder   = @"C:\jobs\error";

        string jobFile = Path.Combine(inFolder, "job_001.txt");

        try
        {
            JobFileMover.MoveToWorking(jobFile, workingFolder);

            // ここで実際の処理を行う想定

            string workingFile = Path.Combine(workingFolder, "job_001.txt");
            JobFileMover.MoveToDone(workingFile, doneFolder);
        }
        catch (Exception)
        {
            string workingFile = Path.Combine(workingFolder, "job_001.txt");
            if (File.Exists(workingFile))
            {
                JobFileMover.MoveToError(workingFile, errorFolder);
            }
        }
    }
}
C#

ここで深掘りしたいのは、「フォルダ移動=状態遷移」という考え方です。
こうしておくと、運用担当者がフォルダを見ただけで「どのファイルが未処理で、どれがエラーか」が一目で分かるようになります。


例外とエラー処理を意識した移動

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

ファイル移動でも、コピーと同様にさまざまな例外が発生する可能性があります。
移動元が存在しない、移動先のフォルダが存在しない、権限がない、移動先に同名ファイルがある、別プロセスがファイルをロックしている、などです。

例外をある程度分類して扱う例を見てみましょう。

using System;
using System.IO;

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

        try
        {
            File.Move(sourcePath, destPath);
            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.Move の基本的な挙動(移動先に同名ファイルがあるときは例外になる、上書きオプションはない)を正しく理解すること。
移動元の存在チェック、移動先ディレクトリの存在確認・自動作成、上書き可否の判断をユーティリティ化して、同じパターンを安全に再利用できるようにすること。
フォルダ構成を「状態」として設計し、未処理・処理中・処理済み・エラーなどをフォルダ移動で表現すると、運用やトラブルシュートが格段にやりやすくなること。
例外やエラーの原因をある程度分類し、ログやメッセージで「何が起きたのか」を分かるようにしておくこと。

ここまで押さえておけば、「ファイルを移動したつもりが消えていた」「どこに行ったか分からない」といった、現場でよくあるトラブルをかなり減らせます。

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