1. はじめに
FileStreamとか他のストリーム系クラスでファイルを扱うとき、ファイルを開くと何が起きるかを事前にちゃんと理解しておくのが超大事なんだ。よくあるのが、ファイルがすでに存在してて、新しいデータを追加したいけど古いデータは消したくないとか、逆に全部上書きしたいとか。逆に、まだファイルが作られてなくて、読み取りだけしたい場合、うっかり空ファイルを作っちゃうのは避けたいよね。
例えば、日記アプリみたいなのを作ってて、毎日新しいメモを既存ファイルの末尾に追加したいなら、追加モードで開く必要がある。他のケース、例えばログファイルをアプリ起動ごとに新しく作り直したいなら、開いたときに中身を消すか、なければ新規作成するモードが必要。逆に、ただ既存ファイルを読むだけなら、ファイルがなければ新規作成しないようにしたい。そうしないと、空ファイルができちゃうかも。
こういう「追加」「上書き」「作らずに読み取り」みたいな色んなシチュエーションは、C#だとSystem.IO.FileMode列挙体で指定するファイルオープンモードで実現できる。それぞれのモードが、ファイルを開く・作るときの挙動をコントロールしてくれるから、思い通りにファイル操作できるんだ。
2. FileMode列挙体 — ざっくり全部解説
これがFileMode列挙体の主な値だよ:
| 値 | 説明 | ファイルが存在する場合 | ファイルが存在しない場合 |
|---|---|---|---|
|
新しいファイルを作る。すでにファイルがあれば例外を投げる | エラー | OK、新規作成 |
|
ファイルがなければ新規作成、あれば上書き | 上書き | OK、新規作成 |
|
既存ファイルを開く。なければ例外 | OK、開く | エラー |
|
あれば開く、なければ新規作成 | OK、開く | OK、新規作成 |
|
既存ファイルを開いて中身を空にする | OK、ファイルを空に | エラー |
|
末尾に追加書き込み用で開く。なければ新規作成 | OK、末尾に追加 | OK、新規作成 |
イラスト:シナリオ別挙動
flowchart TD
Start[スタート]
A{ファイルある?}
B1[追加書き込みで開く]
B2[新規ファイル作成]
B3[上書き]
B4[ファイルを開く]
B5[エラー:ファイル見つからない]
B6[エラー:ファイルすでに存在]
Start --> A
A --Append, OpenOrCreate--> B1
A --Create, Truncate--> B3
A --Open, Truncate--> B4
A --CreateNew--> B6
A --Create, OpenOrCreate, Append--> B2
A --Open, Truncate--> B5
3. ファイルを色んなモードで開く例
じゃあ実際に、コースで作ってるミニアプリでやってみよう。テキストファイルに挨拶と日付を書き込むやつね。よくある3パターンを見てみよう:
- A. 新規ファイル作成(CreateNew)
- B. ファイルを上書き(Create)
- C. ファイル末尾に追加(Append)
新規ファイル作成(FileMode.CreateNew)
ファイルがすでにあったら、このモードは絶対に新規作成できない。IOExceptionが投げられる。データを絶対消したくない、他人のファイルをうっかり消したくないときにピッタリ。
using System;
using System.IO;
string filePath = "greeting.txt";
// 新規ファイル作成を試みる。すでにあれば例外!
try
{
var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("やっほー!これは最初の書き込み。");
writer.WriteLine($"日付: {DateTime.Now}");
Console.WriteLine("ファイル作成成功!");
writer.Close();
}
catch (IOException)
{
Console.WriteLine($"ファイル '{filePath}' はすでに存在!上書きしないよ。");
}
ポイント:このコードを2回目以降に実行するとcatchが動いて、データは消えないよ。
ファイルを上書き(FileMode.Create)
このモードは遠慮なし。ファイルがあれば全部消して上書き。なければ新規作成。毎回「まっさら」から始めたいときに便利。例えばレポートの再保存とか。
using System;
using System.IO;
string filePath = "greeting.txt";
// ファイルを新規作成または完全上書き
var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("やっほー!ファイルを完全に更新したよ :)");
writer.WriteLine($"更新日: {DateTime.Now}");
Console.WriteLine("ファイルを作成または上書きしたよ。");
writer.Close();
何回実行しても、ファイルには最新の内容だけが残る。古いのは全部消えるよ。
ファイル末尾に追加(FileMode.Append)
ログやイベント記録、アクセス履歴とかに最高のモード。ファイルがなければ新規作成、あれば末尾に追加。
using System;
using System.IO;
string filePath = "greeting.txt";
// ファイル末尾に新しい行を追加
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine($"また挨拶!日付: {DateTime.Now}");
Console.WriteLine("ファイル末尾に新しい行を追加したよ。");
writer.Close();
何回も実行してみて。ファイルがどんどん大きくなって、毎回新しい行が末尾に追加されるよ。
4. どのファイルオープンモードを選ぶ?実践アドバイス
リアルなシナリオ例:
- ログ記録:ほぼ必ずAppendを使おう。上書きでログが消えるのは管理者の悪夢&悪者のご褒美。
- レポート出力:たいていCreateでOK。古いバージョンでフォルダがごちゃごちゃしない。
- 絶対消したくないデータのエクスポート:ここはCreateNewがベスト。ファイルがあればエラー、何も触らない。
- ファイルの読み取り:作成モードは不要。FileMode.Openを使おう。なければ問題発生のサイン。
C#でコード書くときは、Fileクラスの静的メソッドもよく使うよね。これらは内部で適切なモードを自動で選んでくれる。例えば、File.AppendAllTextは常にAppend、File.WriteAllTextはCreateで動く。
5. ファイルモードとトラブルあるある
ちょっとした落とし穴:
FileMode.CreateやFileMode.Truncateで開くと、中身は全部消える。うっかりデータ消失は、初心者だけじゃなくベテランでもやらかすから注意!
FileMode.OpenやFileMode.Truncateで存在しないファイルを開こうとすると、FileNotFoundExceptionが出る。File.Exists(path)でファイルがあるか事前にチェックしよう。
Appendは書き込み専用。読み書き両方したいなら、他のモード(OpenOrCreate+適切なFileAccess)を使おう。
例:読み書き両方
たまに、ファイルを読み書き両方したいこともある(例えば銀行口座ファイルで入金も残高確認もしたいとか)。そんなときはFileMode.OpenOrCreateを使おう:
using System.IO;
string filePath = "balance.txt";
// 読み書き両方で開く。なければ新規作成
var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
// ここで読み書きできる
6. 便利な小ネタ
ファイルオープンモードまとめ
graph TD
A[ファイルリクエスト] --> B[モード選択]
B --> C1[CreateNew] -->|ファイルあり| D1[エラー]
C1 -->|ファイルなし| E1[新規作成]
B --> C2[Create] -->|常に| F1[新規作成/上書き]
B --> C3[Open] -->|ファイルあり| G1[開く]
C3 -->|ファイルなし| D1
B --> C4[OpenOrCreate] -->|ファイルあり| G1
C4 -->|ファイルなし| E1
B --> C5[Append] -->|ファイルあり| H1[追加書き込みで開く]
C5 -->|ファイルなし| E1
B --> C6[Truncate] -->|ファイルあり| I1[中身を空に]
C6 -->|ファイルなし| D1
ファイルオープンモードまとめ表
| FileMode | ファイルあり | ファイルなし | データアクセス | 古い内容を消す? |
|---|---|---|---|---|
|
例外 | 新規作成 | 書き込み | 該当なし |
|
上書き | 新規作成 | 書き込み | はい |
|
開く | 例外 | 読み書き | いいえ |
|
開く | 新規作成 | 読み書き | いいえ |
|
中身を空に | 例外 | 書き込み | はい |
|
末尾に追加書き込みで開く | 新規作成 | 書き込みのみ | いいえ(追加) |
Fileクラスの高レベルメソッドとの比較
- File.WriteAllText(path, text) → 内部的にFileMode.Createを使う。つまり古いファイルは上書き。
- File.AppendAllText(path, text) → Appendモードで末尾に追加。
- File.ReadAllText(path) → 読み取り用に開く(FileMode.Open)。
これらのメソッドだけで十分なことも多いけど、FileStreamで明示的にモード指定すると、もっと細かくコントロールできるよ。
OSごとの注意点
Windowsだと、他のプロセスが書き込みでファイルを開いてると、再度開こうとしてもエラーになることが多い。だから、FileShareパラメータをちゃんと使って、ファイルの共有アクセスをコントロールするのが大事(この話はまた別のレクチャーで詳しくやるよ)。OSによってロックや権限の仕組みは違うけど、ファイルオープンモードの考え方自体はどこでも共通だよ。
GO TO FULL VERSION