1. はじめに
移動操作が必要になるシナリオはたくさんあります:一時フォルダからドキュメントフォルダへ"ダウンロード済み"ファイルを移す、写真を一括で整理する、テンプレートに従ってリネームする、メールやログを仕分けするなど。ファイルシステムは秩序を好みます — まあ、少なくともそれっぽいものを。良い開発者はその秩序を作れるべきです。
さらにこういった課題は面接で出されることも多いです:「すべてのファイルにバージョン番号を付けてリネームするユーティリティを書いてください」や「古いドキュメントをアーカイブに移動するコマンドを作ってください」など。では、C#でどうやるか見ていきましょう。
可能な方法の概要
.NETには移動・リネームの主な方法が2つあります — 静的メソッドを使う方法(File.Move, Directory.Move)か、インスタンスメソッドを使う方法(FileInfo.MoveTo, DirectoryInfo.MoveTo)。内部的にはほとんど同じことをしていて、主にシンタックスの違いがあります。
.NETは移動とリネームをどう扱うか?
注意!リネームと移動は本質的に同じ操作です:ファイルやフォルダはパスで識別されます。パスが変われば、そのオブジェクトは「移動した」か「リネームした」とみなされます。もし同じディレクトリ内で名前だけ変えればリネーム、パス全体を変えれば移動です。シュレーディンガーみたいに、新しいパスに新しい名前を指定すれば両方に当てはまります!
2. ファイルの移動とリネーム:例
シンプルな例から進めて、ミニファイルマネージャーをベースに説明します。
静的メソッド File.Move
ファイルを移動(またはリネーム)する例:
// ファイル test1.txt を別のフォルダに新しい名前で移動する
File.Move(@"C:\Temp\test1.txt", @"C:\Archive\test1_archived.txt");
同じフォルダ内で名前だけ変えたい場合:
// 単にファイル名を変更する
File.Move(@"C:\Temp\test1.txt", @"C:\Temp\test_renamed.txt");
これだけです。.NETは単に一つのパスを切り取り、別のパスをオブジェクトに割り当てます。両方のパスが利用可能で、衝突がなければ動作します。
静的メソッド Directory.Move
フォルダも同様です:
// フォルダを移動する
Directory.Move(@"C:\Temp\OldFolder", @"C:\Temp\NewFolder");
同じディレクトリ内でリネームすることも、別の場所へ丸ごと移動することもできます。
3. インスタンスメソッド: FileInfo.MoveTo と DirectoryInfo.MoveTo
すでに FileInfo や DirectoryInfo オブジェクトがある場合は、MoveTo() を呼べます:
var fileInfo = new FileInfo(@"C:\Temp\test1.txt");
fileInfo.MoveTo(@"C:\Archive\test1_archived.txt");
// フォルダ:
var dirInfo = new DirectoryInfo(@"C:\Temp\OldFolder");
dirInfo.MoveTo(@"C:\Temp\NewFolder");
ここでも同じ:新しいパスが同じディレクトリで名前だけ変わるならリネーム、別のディレクトリなら移動です。
4. もし既に同じ場所にファイルがあったら?
ここで細かい問題が出てきます — たいてい一番悪いタイミングで噛みつきます。
File.Move
宛先パスに既にファイルがあると、IOException がスローされてアプリが落ちます:
try
{
File.Move("data.txt", "archive\\data.txt");
}
catch (IOException ex)
{
Console.WriteLine("宛先のファイルは既に存在します! " + ex.Message);
}
FileInfo.MoveTo
同じく、宛先にファイルがあると IOException になります。
Directory.Move
フォルダの場合はさらに厳しく、ターゲットフォルダが既に存在すると例外が投げられます。占有されているフォルダには移動できません。
教訓:移動先に同名のファイルやフォルダがないか必ず確認しましょう!
// 宛先にファイルがあるかを落ち着いてチェックする
if (!File.Exists("archive\\data.txt"))
{
File.Move("data.txt", "archive\\data.txt");
}
else
{
Console.WriteLine("宛先のファイルは既に存在します!");
}
5. 役に立つポイント
フォルダのリネーム例
var oldDir = @"C:\Projects\OldReport";
var newDir = @"C:\Projects\NewReport";
// 新しいフォルダがないことと古いフォルダがあることをチェック
if (Directory.Exists(oldDir) && !Directory.Exists(newDir))
{
Directory.Move(oldDir, newDir);
Console.WriteLine("フォルダのリネームに成功しました。");
}
else
{
Console.WriteLine("エラー: 元フォルダが見つからないか、新しいフォルダが既に存在します。");
}
ネストされた内容を含むリネーム
フォルダの中身(サブフォルダやファイル)は追加のコマンドなしで一緒に移動します!便利ですね:1つのコマンドでどんな深さのフォルダでも新しい場所に移動できます。
ベストプラクティスと Path クラスの便利メソッド
ファイルをリネーム・移動するときは、パスを組み立てたりファイル名を変更するために System.IO.Path のメソッドを使いましょう:
- 拡張子なしのファイル名を取得する: Path.GetFileNameWithoutExtension(path)
- 拡張子だけを置き換える: Path.ChangeExtension(path, ".bak")
- 別名で新しいパスを組み立てる: Path.Combine(Path.GetDirectoryName(path), "newname.txt")
string oldPath = @"C:\Reports\2023_Final.docx";
string newName = "2023_Archive.docx";
string newPath = Path.Combine(Path.GetDirectoryName(oldPath)!, newName);
File.Move(oldPath, newPath);
6. 重要な注意点とよくあるミス
アクセス権とロック
パスへの書き込み権限がないと UnauthorizedAccessException が投げられます。ファイルが他のプロセスで使われている(例:Wordで開かれている)場合もエラーになります。これは非常に頻繁に起きる問題です。
異なるドライブやボリューム間の移動
実際には、File.Move は同一ボリューム内では高速なリネーム操作を行います。CドライブからDドライブへ移そうとすると、.NETは自動的にコピーして元を削除する処理を行います。もし宛先のディスクが満杯などで失敗したら、元のファイルは残ります。
興味深いことに:ほとんどの現代のファイルシステムは同一ボリューム内での「高速なリネーム/移動」をサポートし、ボリューム間ではコピー+削除になります。
フォルダ丸ごとの移動(Directory.Move)
フォルダを自分自身の中や自分のサブフォルダに移動することはできません(例:C:\Data を C:\Data\Archive に移すなど)— これはエラーになります。
安全に移動するには?
特に大量に移動する前には、必ずソースの存在をチェック(File.Exists, Directory.Exists)し、宛先に衝突がないことを確認してください。そして忘れずに try-catch を使いましょう:ファイルシステムは気まぐれで、予期せぬタイミングでエラーを出すことがあります。
GO TO FULL VERSION