1. はじめに
ストリームを直接扱う(StreamReader, StreamWriter, FileStream)ことはとても有用です。低レベルで読み書きのプロセスを制御したいとき、例えば「少しずつブロックを書き込む」や大きなファイルを部分ごとに処理する場合に役立ちます。
でも現実には「このファイルは存在するか?」、「ファイルを別の場所にコピーする」、「ファイルを削除する」、「フォルダ内のすべての .txt ファイルを取得する」など、単純な作業が多いです。そういう場合のために Microsoft は二つの便利な道具を用意しました:静的クラスの File と Directory。静的なのでインスタンス化不要、クラス名から直接メソッドを呼べます。
日常のたとえ
ストリームが「あなたが鉛筆で一文字ずつ丁寧に書く」なら、File と Directory は「引き出し付きの机」です。引き出し(Directory)をさっと開けて紙を取り出す(File.ReadAllText)、紙を捨てる(File.Delete)、引き出しごと別の場所に移す(Directory.Move)といったイメージです。シンプルな作業にはシンプルなメソッドで十分なんです。
2. クラス File:何で何に使うか
File クラスには、一般的なファイル操作のための便利な静的メソッド群があります。
よく使うメソッド
| メソッド | 説明 |
|---|---|
|
指定パスにファイルがあるか確認する |
|
ファイル全体をテキストとして読み取る |
|
ファイルの全行を配列として読み取る |
|
ファイルを上書きしてテキストを書き込む |
|
テキストをファイルの末尾に追記する |
|
ファイルをコピーする |
|
ファイルを削除する |
|
ファイルを移動または名前変更する |
|
複雑な操作のためにストリームを開く |
簡単な例:ファイルの存在を確認して読み込む
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("ファイルの内容:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("ファイルが見つかりません!");
}
見ての通り、単純なケースではストリームを手動で扱う必要はありません!
3. クラス Directory:フォルダを楽に管理する
File が個々のファイル向けなら、Directory はフォルダ(ディレクトリ)向けです。
基本的なタスク
| メソッド | 説明 |
|---|---|
|
フォルダがあるか確認する |
|
フォルダを作成する(中間フォルダも含む) |
|
フォルダを削除する(サブディレクトリを含めることも可能) |
|
フォルダ内のファイル一覧を取得する |
|
サブディレクトリ一覧を取得する |
|
フォルダを移動または名前変更する |
|
アプリケーションの現在の作業ディレクトリを取得する |
例:フォルダを作ってそこにファイルを保存する
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("フォルダ 'Results' を作成しました。");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "集計データ: ...");
Console.WriteLine($"ファイル {filePath} を書き込みました。");
4. File と Directory の典型的な使用例
基本メソッドだけだと退屈に見えるかもしれませんが、実際に使うと便利です。ここでは実例を通して、簡単なアプリ(簡易 todo リスト)を想定して進めます。結果はファイルに保存します。
データをファイルに保存(既存ファイルを置き換え)
// タスク一覧をファイルに保存
string[] todos = { "パンを買う", "医者に電話する", "C#の宿題をやる" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("やることリストを保存しました。");
ファイルからデータを読み込む
// ファイルがあればタスクを読み込む
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("あなたのやることリスト:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("まだやることリストはありません。");
}
新しいタスクを追加(ファイルを上書きしない)
string newTask = "犬と散歩する";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("タスクを追加しました!");
ファイルのコピーとバックアップ
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("やることリストのバックアップを作成しました。");
ファイルの移動(または名前変更)
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("やることリストをアーカイブしました(名前変更)。");
ファイルの削除
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("アーカイブを削除しました — スペースを開放!");
}
5. フォルダ操作:例
ディレクトリ階層の作成
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"フォルダ {path} を作成しました(中間フォルダも含む)。");
ファイルとフォルダの一覧取得
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("フォルダ内のファイル:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("サブディレクトリ:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
ワイルドカードでファイルをフィルタ(.txt のみ)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine(".txt ファイルのみ:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
再帰的走査(すべてのサブフォルダ内のファイル)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("すべての入れ子になったフォルダ内のファイル:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
ディレクトリの移動と削除
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"フォルダ {from} を {to} に移動しました");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true: 中身ごと削除
Console.WriteLine($"フォルダ {to} を(中身ごと)削除しました。");
}
6. 便利なポイント
パス関連の補助:クラス Path
ファイルやフォルダを扱うとき、フルパスを組み立てたり、ディレクトリ名とファイル名を結合したり、拡張子を解析したり、不正な文字をチェックしたりする必要があります。そのための補助クラスが Path です。
例
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // 安全な結合!
Console.WriteLine(fullPath); // "Results/week1.txt"(Windowsでは "\")
- 拡張子を取得する: Path.GetExtension(fullPath)
- パスなしのファイル名を取得する: Path.GetFileName(fullPath)
文字列の連結でパスを作る代わりに Path.Combine を使いましょう — クロスプラットフォームで安全です。
「一度に全部」系メソッドとストリーム版の違い
多くの File / Directory メソッド(例: File.ReadAllText, File.WriteAllText, Directory.GetFiles)は「一度に全部」を行います:読み取り/書き込み/一覧取得を単一の操作で行います。とても便利ですが、大きすぎるファイルや巨大なディレクトリではメモリが足りなくなることがあります。ギガバイト単位のデータを扱うなら、ストリームクラス(StreamReader, StreamWriter, FileStream など)や分割処理に切り替えてください。
File/Directory を使うべき時とストリームを使うべき時
| 状況 | 使うもの |
|---|---|
| ファイル/フォルダの存在を確認したい | |
| 小さめのファイルをまるごと読み書きしたい | |
| ファイルの末尾にテキストを追加したい | |
| ディレクトリ内のファイル一覧を取得したい | |
| ファイルをコピー/削除/移動したい | |
| フォルダごとコピー/削除したい | Directory.Delete/Move/Copy(Copy はサードパーティライブラリが必要なことが多い) |
| 非常に大きなファイルを部分ごとに処理したい | ストリームクラス(StreamReader, FileStream) |
実務的な価値と利用ケース
- 設定や構成、JSON/XML ファイルの高速な読み書き。
- ログシステム(ディスクへのログ書き込み)。
- 簡易的なバックアップやデータ移行ユーティリティ。
- マルチメディア、写真、ドキュメントのフォルダスキャン。
- テンプレート読み込み、エクスポート/インポート、レポート生成などのユーザー機能。
面接の課題でもこれらのクラスを使う問題がよく出ます:「あるディレクトリ内の .txt ファイルの合計サイズを計算する関数を実装せよ」「ファイルを別フォルダにバックアップする処理を作れ」などです。
視覚的な早見表:どのメソッドを使うか
graph TD
A[何をしたい?]
A -->|存在確認| B[File.Exists または Directory.Exists]
A -->|ファイルをまるごと読み書き| C[File.ReadAllText/WriteAllText]
A -->|ファイルにデータを追加| D[File.AppendAllText]
A -->|フォルダ操作| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|ファイルのコピー/削除/移動| F[File.Copy/Delete/Move]
A -->|フォルダごとコピー/削除| G[Directory.Delete/Move]
7. 特徴とよくあるエラー
存在確認とレースコンディション。存在をチェックしても、その直後の操作までに別プロセスが状況を変える可能性があります。だから存在確認をしていても、実際の操作は必ず try-catch で囲んでください。
アクセス権。アプリに読み取り/書き込み/削除の権限がないと、メソッドは UnauthorizedAccessException を投げます。
パスが長すぎる。パスの最大長(古い Windows では通常 260 文字)が問題になることがあります。.NET 9 では制限が緩和されていますが、注意は必要です。
例外処理。File.ReadAllText や Directory.GetFiles のようなメソッドはエラーに厳格です:ファイルやフォルダがないと例外を投げます。事前チェックするか、try-catch で安全に扱ってください。
GO TO FULL VERSION