C# FileStreamの使い方を初心者向けに解説|ファイルの読み込み・書き込み・usingによる安全な閉じ方まで

はじめに

C#でファイルを読み込んだり、ファイルに書き込んだりするときによく使われるのがFileStreamです。FileStreamを使うと、テキストファイルだけでなく、画像、PDF、音声ファイルなどのバイナリファイルも扱えます。

一方で、初心者にとっては「FileStreamとは何か」「File.ReadAllTextStreamReaderと何が違うのか」「ファイルを閉じ忘れるとどうなるのか」といった点が分かりにくいこともあります。

この記事では、C#のFileStreamの基本から、ファイルの読み込み、書き込み、追記、usingによる安全な閉じ方、よくあるエラーと対処法まで、初心者向けに順番に解説します。

1. C#のFileStreamとは?ファイル操作の基本を初心者向けに解説

FileStreamは、C#でファイルをバイト単位で読み書きするためのクラスです。.NETではファイルやストリームを扱うための型がSystem.IO名前空間に用意されており、FileStreamもその中に含まれます。FileStreamではReadWriteCopyToFlushなどの同期処理に加えて、ReadAsyncWriteAsyncなどの非同期処理も利用できます。Microsoft Learn+1

1-1. FileStreamでできること

FileStreamを使うと、主に次のようなファイル操作ができます。

C#
// ファイルを開く
FileStream fs = new FileStream("sample.txt", FileMode.Open);

// ファイルを閉じる
fs.Close();

FileStreamでは、ファイルを開いて、指定した位置からデータを読み込んだり、指定したデータを書き込んだりできます。文字列そのものではなく、基本的にはbyte[]、つまりバイト配列を使って処理する点が特徴です。

たとえば、テキストファイルの内容も、コンピューター内部ではバイト列として保存されています。FileStreamはそのバイト列を直接扱うため、テキストファイルだけでなく、画像やPDFのようなバイナリファイルにも対応できます。

1-2. FileStreamを使う場面

FileStreamは、ファイルを細かく制御したいときに使います。

たとえば、次のような場面です。

C#
// 大きなファイルを少しずつ読み込む
// 画像やPDFなどのバイナリファイルをコピーする
// ファイルの共有方法を指定して開く
// 読み込み専用、書き込み専用などのアクセス権を指定する

小さなテキストファイルを一括で読み込むだけなら、File.ReadAllTextのほうが簡単です。しかし、大容量ファイルを扱う場合や、メモリ使用量を抑えたい場合、バイナリファイルを扱う場合にはFileStreamが役立ちます。

1-3. Fileクラス・StreamReader・StreamWriterとの違い

C#には、ファイル操作に使えるクラスが複数あります。代表的なものがFileクラス、FileStreamStreamReaderStreamWriterです。

Fileクラスは、ファイルの読み書きを簡単に行うための静的メソッドを提供します。たとえば、File.ReadAllTextを使えば、ファイル全体を文字列として一度に読み込めます。

C#
string text = File.ReadAllText("sample.txt");

一方、FileStreamはファイルをストリームとして開き、バイト単位で読み書きします。

C#
using FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.Read);

StreamReaderはテキストを読み込むためのクラスで、StreamWriterはテキストを書き込むためのクラスです。テキストファイルを扱うだけなら、StreamReaderStreamWriterのほうが分かりやすい場合があります。

C#
using StreamReader reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();

つまり、初心者は次のように考えると分かりやすいです。

FileStreamはバイト単位で細かく扱いたいときに使い、StreamReaderStreamWriterはテキストを文字列として扱いたいときに使います。

1-4. テキストファイルとバイナリファイルの違い

テキストファイルとは、人間が文字として読める内容を保存したファイルです。たとえば、.txt.csv.json.xmlなどが該当します。

一方、バイナリファイルとは、画像、PDF、動画、音声、実行ファイルなど、文字列としてそのまま読むことを前提としていないファイルです。

FileStreamはバイト単位でファイルを扱うため、テキストファイルにもバイナリファイルにも使えます。ただし、テキストファイルを文字列として扱う場合は、Encoding.UTF8などの文字コードを意識する必要があります。

2. FileStreamを使うための準備

2-1. System.IO名前空間を読み込む

FileStreamを使うには、通常はファイルの先頭でSystem.IO名前空間を読み込みます。

C#
using System.IO;

文字列とバイト配列を変換する場合は、System.Textも使います。

C#
using System.IO;
using System.Text;

たとえば、UTF-8で文字列を書き込む場合は、次のようにEncoding.UTF8を使います。

C#
byte[] bytes = Encoding.UTF8.GetBytes("こんにちは");

2-2. ファイルパスの指定方法

FileStreamでは、操作したいファイルのパスを文字列で指定します。

C#
string path = "sample.txt";
using FileStream fs = new FileStream(path, FileMode.Open);

Windows環境で絶対パスを指定する場合は、次のように書けます。

C#
string path = @"C:\work\sample.txt";

@を付けた文字列は逐語的文字列リテラルと呼ばれ、\をエスケープせずに書けます。@を使わない場合は、次のように\\\として書く必要があります。

C#
string path = "C:\\work\\sample.txt";

2-3. 相対パスと絶対パスの違い

相対パスは、実行中のプログラムの基準フォルダから見たファイルの位置を指定する方法です。

C#
string path = "data/sample.txt";

絶対パスは、ドライブ名やルートディレクトリから始まる完全なパスです。

C#
string path = @"C:\work\data\sample.txt";

初心者がつまずきやすいのは、相対パスの基準位置です。Visual Studioやdotnet runで実行する場合、実行ファイルの出力先フォルダが基準になることがあります。ファイルが見つからない場合は、まず実際にどのフォルダを基準にしているか確認しましょう。

C#
Console.WriteLine(Environment.CurrentDirectory);

2-4. サンプル用ファイルを用意する

この記事のサンプルでは、次のようなsample.txtを使います。

Hello C#
こんにちは FileStream

プログラムと同じ実行フォルダにsample.txtを置くか、コード内で正しいファイルパスを指定してください。

3. FileStreamの基本構文と主要な引数

3-1. FileStreamの基本的な書き方

FileStreamの基本的な書き方は次のとおりです。

C#
using FileStream fs = new FileStream(
"sample.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Read
);

FileStreamのコンストラクタでは、ファイルパス、作成モード、読み書きの権限、共有方法などを指定できます。代表的なコンストラクタでは、パス、FileModeFileAccessFileShareを指定してFileStreamのインスタンスを作成します。Microsoft Learn+1

3-2. FileModeとは

FileModeは、ファイルをどのように開くかを指定する列挙型です。既存ファイルを開くのか、新しく作成するのか、上書きするのか、追記するのかを決めます。Microsoftのドキュメントでも、FileMode.Openを使って既存ファイルを開く例が示されています。Microsoft Learn

よく使うFileModeは次のとおりです。

C#
FileMode.Open        // 既存ファイルを開く。存在しないとエラー
FileMode.Create // 新規作成。既存ファイルがあれば上書き
FileMode.CreateNew // 新規作成。既存ファイルがあればエラー
FileMode.OpenOrCreate// あれば開く。なければ作成
FileMode.Append // 末尾に追記
FileMode.Truncate // 既存ファイルを空にして開く

初心者がよく使うのは、読み込みならFileMode.Open、上書きならFileMode.Create、追記ならFileMode.Appendです。

3-3. FileAccessとは

FileAccessは、ファイルに対して何を行うかを指定します。

C#
FileAccess.Read      // 読み込み専用
FileAccess.Write // 書き込み専用
FileAccess.ReadWrite // 読み書き両方

読み込みだけでよい場合は、FileAccess.Readを指定します。書き込みだけでよい場合は、FileAccess.Writeを指定します。

C#
using FileStream fs = new FileStream(
"sample.txt",
FileMode.Open,
FileAccess.Read
);

必要以上にFileAccess.ReadWriteを使うと、権限エラーやファイル共有の問題が起きやすくなることがあります。基本的には、必要最小限の権限を指定しましょう。

3-4. FileShareとは

FileShareは、自分がファイルを開いている間に、他の処理がそのファイルへアクセスできるかを指定します。FileShareは、他のFileStreamなどが同じファイルに対してどのようなアクセスを許可されるかを指定するための値です。Microsoft Learn

よく使う値は次のとおりです。

C#
FileShare.None      // 他からのアクセスを許可しない
FileShare.Read // 他からの読み込みを許可する
FileShare.Write // 他からの書き込みを許可する
FileShare.ReadWrite // 他からの読み書きを許可する

たとえば、自分は読み込みだけ行い、他の処理にも読み込みを許可したい場合は次のように書きます。

C#
using FileStream fs = new FileStream(
"sample.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Read
);

3-5. FileStreamでよく使うコンストラクタ

初心者がまず覚えるとよいコンストラクタは、次の3つです。

C#
new FileStream(path, FileMode.Open);

new FileStream(path, FileMode.Open, FileAccess.Read);

new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);

より実用的には、FileAccessFileShareまで指定する書き方がおすすめです。

C#
string path = "sample.txt";

using FileStream fs = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.Read
);

4. FileStreamでファイルを読み込む方法

4-1. Readメソッドでバイト単位に読み込む

FileStreamでファイルを読み込むには、Readメソッドを使います。Readは、ファイルからバイト列を読み込み、読み込んだバイト数を返します。FileStreamでは同期処理としてReadを使えるほか、非同期処理ではReadAsyncを使えます。Microsoft Learn+1

C#
using System.IO;

string path = "sample.txt";

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

byte[] buffer = new byte[1024];

int bytesRead = fs.Read(buffer, 0, buffer.Length);

Console.WriteLine($"読み込んだバイト数: {bytesRead}");

このコードでは、最大1024バイトまで読み込めるバッファを用意し、Readメソッドでファイルの内容を読み込んでいます。

4-2. 読み込んだバイト配列を文字列に変換する

テキストファイルを読み込んだ場合、読み込んだバイト配列を文字列に変換する必要があります。

C#
using System.IO;
using System.Text;

string path = "sample.txt";

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

byte[] buffer = new byte[1024];
int bytesRead = fs.Read(buffer, 0, buffer.Length);

string text = Encoding.UTF8.GetString(buffer, 0, bytesRead);

Console.WriteLine(text);

ポイントは、Encoding.UTF8.GetStringbytesReadを渡すことです。バッファ全体を文字列に変換すると、実際には読み込んでいない部分まで含まれてしまう可能性があります。

4-3. Encodingを指定して文字化けを防ぐ

日本語を含むテキストファイルでは、文字コードを意識する必要があります。ファイルがUTF-8で保存されているなら、Encoding.UTF8を使います。

C#
string text = Encoding.UTF8.GetString(buffer, 0, bytesRead);

Shift_JISのファイルを扱う場合は、環境によって追加の設定が必要になることがあります。初心者のうちは、ファイルをUTF-8で保存し、C#側でもEncoding.UTF8を指定するのが分かりやすいです。

文字化けが起きる主な原因は、保存時の文字コードと読み込み時の文字コードが一致していないことです。

4-4. ファイルが存在しない場合の注意点

FileMode.Openで存在しないファイルを開こうとすると、FileNotFoundExceptionが発生します。

C#
string path = "notfound.txt";

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

ファイルが存在するか確認してから開く場合は、File.Existsを使います。

C#
string path = "sample.txt";

if (File.Exists(path))
{
using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
Console.WriteLine("ファイルを開きました。");
}
else
{
Console.WriteLine("ファイルが存在しません。");
}

ただし、File.Existsで確認した直後に別の処理がファイルを削除する可能性もあります。そのため、実用的なコードでは例外処理も組み合わせると安全です。

C#
try
{
using FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.Read);
}
catch (FileNotFoundException)
{
Console.WriteLine("ファイルが見つかりませんでした。");
}

4-5. StreamReaderを組み合わせて読み込む方法

テキストファイルを読み込む場合は、FileStreamStreamReaderを組み合わせると便利です。

C#
using System.IO;
using System.Text;

string path = "sample.txt";

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
using StreamReader reader = new StreamReader(fs, Encoding.UTF8);

string text = reader.ReadToEnd();

Console.WriteLine(text);

FileStreamだけで読む場合はバイト配列を扱いますが、StreamReaderを使うと文字列として読み込めます。テキスト処理ではこちらのほうが読みやすいコードになります。

5. FileStreamでファイルに書き込む方法

5-1. Writeメソッドでバイト単位に書き込む

FileStreamでファイルに書き込むには、Writeメソッドを使います。FileStreamでは同期処理としてWriteを使えるほか、非同期処理ではWriteAsyncを使えます。Microsoft Learn+1

C#
using System.IO;
using System.Text;

string path = "output.txt";

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);

byte[] bytes = Encoding.UTF8.GetBytes("Hello FileStream");

fs.Write(bytes, 0, bytes.Length);

このコードを実行すると、output.txtが作成され、Hello FileStreamという内容が書き込まれます。

5-2. 文字列をバイト配列に変換して書き込む

FileStreamは文字列を直接書き込むのではなく、バイト配列を書き込みます。そのため、文字列を書き込みたい場合は、Encoding.UTF8.GetBytesでバイト配列に変換します。

C#
using System.IO;
using System.Text;

string path = "output.txt";
string text = "こんにちは FileStream";

byte[] bytes = Encoding.UTF8.GetBytes(text);

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);

fs.Write(bytes, 0, bytes.Length);

日本語を含む文字列を書き込む場合も、読み込み時と同じように文字コードを意識しましょう。基本的にはUTF-8で統一するのがおすすめです。

5-3. 新規作成・上書き・追記の違い

ファイルに書き込むときは、FileModeの選び方が重要です。

C#
FileMode.Create      // 新規作成。既存ファイルがあれば上書き
FileMode.CreateNew // 新規作成。既存ファイルがあればエラー
FileMode.Append // 既存ファイルの末尾に追記
FileMode.OpenOrCreate// あれば開く。なければ作成

上書きしたい場合は、FileMode.Createを使います。

C#
using FileStream fs = new FileStream("output.txt", FileMode.Create, FileAccess.Write);

追記したい場合は、FileMode.Appendを使います。

C#
using FileStream fs = new FileStream("output.txt", FileMode.Append, FileAccess.Write);

FileMode.Appendを使うと、既存の内容を残したまま末尾に書き込めます。

5-4. Flushで書き込み内容を反映する

FileStreamは内部的にバッファを使うため、書き込んだ内容がすぐにファイルへ反映されるとは限りません。Flushを呼び出すと、ストリーム内のバッファの内容をファイルへ書き出します。Microsoftのドキュメントでは、FlushAsyncは.NETのストリームバッファをファイルへフラッシュするものとして説明されています。Microsoft Learn

C#
using FileStream fs = new FileStream("output.txt", FileMode.Create, FileAccess.Write);

byte[] bytes = Encoding.UTF8.GetBytes("書き込みテスト");

fs.Write(bytes, 0, bytes.Length);
fs.Flush();

通常はusingFileStreamを閉じるときに必要な処理が行われるため、毎回Flushを書く必要はありません。ただし、ファイルを開いたまま途中で内容を反映したい場合にはFlushを使います。

5-5. StreamWriterを組み合わせて書き込む方法

テキストを書き込む場合は、StreamWriterを組み合わせると簡単です。

C#
using System.IO;
using System.Text;

string path = "output.txt";

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
using StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);

writer.WriteLine("こんにちは");
writer.WriteLine("FileStreamとStreamWriterを使っています。");

StreamWriterを使うと、文字列をバイト配列に変換する処理を自分で書かなくて済みます。テキストファイルへの書き込みでは、FileStream単体よりも分かりやすいコードになることが多いです。

6. usingでFileStreamを安全に閉じる方法

6-1. FileStreamを閉じる必要がある理由

FileStreamでファイルを開いたら、使い終わったあとに必ず閉じる必要があります。ファイルを開いたままにすると、他のプログラムがそのファイルを開けなかったり、書き込み内容が正しく反映されなかったりすることがあります。

特に、書き込み処理では閉じ忘れに注意が必要です。ファイルへの書き込みがバッファに残ったままになると、期待した内容がファイルに保存されない可能性があります。

6-2. CloseとDisposeの違い

Closeはストリームを閉じるためのメソッドです。Disposeは、FileStreamが使用しているリソースを解放するためのメソッドです。FileStream.Disposeは、FileStreamが参照しているマネージドオブジェクトなどのリソースを解放する処理として説明されています。Microsoft Learn

C#
FileStream fs = new FileStream("sample.txt", FileMode.Open);
fs.Close();

ただし、現在のC#では、手動でCloseを書くよりもusingを使うのが一般的です。usingを使うと、処理が終わったときに自動的にDisposeが呼ばれます。

6-3. using文の基本構文

従来のusing文は次のように書きます。

C#
using (FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.Read))
{
// この中でFileStreamを使う
}

usingブロックを抜けると、自動的にFileStreamが破棄されます。例外が発生した場合でもリソースが解放されるため、ファイル操作では基本的にusingを使いましょう。

6-4. usingを使った読み込みサンプル

C#
using System.IO;
using System.Text;

string path = "sample.txt";

using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
int bytesRead = fs.Read(buffer, 0, buffer.Length);

string text = Encoding.UTF8.GetString(buffer, 0, bytesRead);

Console.WriteLine(text);
}

このコードでは、usingブロックを抜けた時点でFileStreamが自動的に閉じられます。

6-5. usingを使った書き込みサンプル

C#
using System.IO;
using System.Text;

string path = "output.txt";
string text = "FileStreamの書き込みサンプルです。";

byte[] bytes = Encoding.UTF8.GetBytes(text);

using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
fs.Write(bytes, 0, bytes.Length);
}

usingを使っているため、書き込みが終わったあとにファイルが安全に閉じられます。

6-6. using varを使ったシンプルな書き方

C#では、using varを使ってよりシンプルに書くこともできます。

C#
using System.IO;
using System.Text;

string path = "output.txt";
string text = "using varを使った書き方です。";

byte[] bytes = Encoding.UTF8.GetBytes(text);

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);

fs.Write(bytes, 0, bytes.Length);

using varで宣言した変数は、そのスコープを抜けると自動的に破棄されます。コードが短くなり、読みやすくなるため、慣れてきたら積極的に使うとよいでしょう。

7. FileStreamでよく使う実践サンプル

7-1. テキストファイルを読み込むサンプル

まずは、FileStreamStreamReaderを組み合わせてテキストファイルを読み込むサンプルです。

C#
using System.IO;
using System.Text;

string path = "sample.txt";

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using StreamReader reader = new StreamReader(fs, Encoding.UTF8);

string text = reader.ReadToEnd();

Console.WriteLine(text);

テキストファイルを読み込むだけなら、このようにStreamReaderを使うと簡潔です。

7-2. テキストファイルに書き込むサンプル

次に、テキストファイルへ書き込むサンプルです。

C#
using System.IO;
using System.Text;

string path = "output.txt";

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
using StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);

writer.WriteLine("1行目のテキストです。");
writer.WriteLine("2行目のテキストです。");

FileMode.Createを指定しているため、既存のoutput.txtがある場合は上書きされます。

7-3. ファイルに追記するサンプル

既存ファイルの末尾に追記する場合は、FileMode.Appendを使います。

C#
using System.IO;
using System.Text;

string path = "log.txt";

using FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write);
using StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);

writer.WriteLine($"{DateTime.Now}: ログを追加しました。");

ログファイルのように、既存の内容を残したまま新しい行を追加したい場合に便利です。

7-4. 画像やPDFなどのバイナリファイルをコピーするサンプル

FileStreamはバイナリファイルのコピーにも使えます。

C#
using System.IO;

string sourcePath = "source.pdf";
string destinationPath = "copy.pdf";

using FileStream source = new FileStream(sourcePath, FileMode.Open, FileAccess.Read);
using FileStream destination = new FileStream(destinationPath, FileMode.Create, FileAccess.Write);

source.CopyTo(destination);

CopyToを使うと、読み込み元のストリームから書き込み先のストリームへデータをコピーできます。画像、PDF、音声ファイルなど、文字列として扱わないファイルのコピーに向いています。

7-5. 大きなファイルを分割して読み込むサンプル

大容量ファイルを扱う場合、ファイル全体を一度にメモリへ読み込むと負荷が高くなります。そのような場合は、一定サイズのバッファを使って少しずつ読み込みます。

C#
using System.IO;

string path = "largefile.dat";

byte[] buffer = new byte[8192];

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

int bytesRead;

while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
Console.WriteLine($"{bytesRead} バイト読み込みました。");

// ここで読み込んだデータを処理する
}

このようにすると、大きなファイルでもメモリ使用量を抑えながら処理できます。

8. FileStreamでよくあるエラーと対処法

8-1. IOExceptionが発生する原因

IOExceptionは、入出力処理で問題が起きたときに発生する例外です。たとえば、ファイルが使用中、ディスクに問題がある、ファイルの読み書き中に何らかのエラーが発生した場合などです。

C#
try
{
using FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.ReadWrite);
}
catch (IOException ex)
{
Console.WriteLine("入出力エラーが発生しました。");
Console.WriteLine(ex.Message);
}

IOExceptionが発生した場合は、ファイルが他のアプリで開かれていないか、パスが正しいか、アクセス権に問題がないかを確認しましょう。

8-2. UnauthorizedAccessExceptionが発生する原因

UnauthorizedAccessExceptionは、アクセス権がない場合に発生します。たとえば、読み取り専用ファイルに書き込もうとしたり、権限のないフォルダにファイルを作成しようとしたりした場合です。

C#
try
{
using FileStream fs = new FileStream(@"C:\Windows\test.txt", FileMode.Create, FileAccess.Write);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("ファイルまたはフォルダへのアクセス権がありません。");
}

対処法としては、書き込み可能なフォルダを指定する、管理者権限が必要な場所を避ける、ファイルの読み取り専用属性を確認する、といった方法があります。

8-3. FileNotFoundExceptionが発生する原因

FileNotFoundExceptionは、指定したファイルが存在しない場合に発生します。

C#
try
{
using FileStream fs = new FileStream("missing.txt", FileMode.Open, FileAccess.Read);
}
catch (FileNotFoundException)
{
Console.WriteLine("指定されたファイルが見つかりません。");
}

読み込み時にFileMode.Openを使う場合は、ファイルが存在している必要があります。存在しない場合に作成したいなら、FileMode.OpenOrCreateを検討します。

C#
using FileStream fs = new FileStream("sample.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);

8-4. ファイルが使用中で開けない場合の対処法

ファイルが他のアプリケーションや別の処理で開かれていると、IOExceptionが発生することがあります。特にFileShare.Noneのように共有を許可しない指定をしている場合、他の処理から開けなくなります。

読み込みだけでよい場合は、FileShare.Readを指定すると、他の読み込み処理と共存しやすくなります。

C#
using FileStream fs = new FileStream(
"sample.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Read
);

ただし、ファイルの内容が途中で変更される可能性がある場合は注意が必要です。ログファイルなどを読む場合は、ファイル共有の指定を状況に合わせて調整しましょう。

8-5. 文字化けする場合の対処法

文字化けが起きる場合は、ファイルの文字コードとC#で指定しているEncodingが一致しているか確認します。

UTF-8で保存されたファイルなら、読み込み時もUTF-8を指定します。

C#
using StreamReader reader = new StreamReader("sample.txt", Encoding.UTF8);
string text = reader.ReadToEnd();

書き込み時も同じようにUTF-8を指定します。

C#
using StreamWriter writer = new StreamWriter("output.txt", false, Encoding.UTF8);
writer.WriteLine("こんにちは");

日本語を扱う場合は、保存時と読み込み時の文字コードを統一することが大切です。

9. FileStreamを使うときの注意点とベストプラクティス

9-1. usingで確実にリソースを解放する

FileStreamを使うときは、基本的にusingを使いましょう。

C#
using FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.Read);

usingを使えば、処理が終わったときに自動的にリソースが解放されます。ファイルの閉じ忘れを防げるため、初心者ほどusingを習慣にすることが重要です。

9-2. 読み書きの権限を必要最小限にする

読み込みだけならFileAccess.Read、書き込みだけならFileAccess.Writeを使います。

C#
// 読み込みだけ
using FileStream readStream = new FileStream("sample.txt", FileMode.Open, FileAccess.Read);

// 書き込みだけ
using FileStream writeStream = new FileStream("output.txt", FileMode.Create, FileAccess.Write);

必要以上にFileAccess.ReadWriteを指定すると、権限エラーやファイル共有の問題が起きやすくなることがあります。

9-3. 大容量ファイルではバッファサイズを意識する

大きなファイルを処理するときは、一度に全体を読み込まず、バッファを使って分割処理します。

C#
byte[] buffer = new byte[8192];

using FileStream fs = new FileStream("largefile.dat", FileMode.Open, FileAccess.Read);

int bytesRead;

while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
// 読み込んだ分だけ処理する
}

バッファサイズは処理内容によって調整します。一般的には、数KBから数十KB程度のバッファを使うことが多いです。

9-4. テキスト処理ではStreamReader・StreamWriterも検討する

テキストファイルを扱う場合、FileStreamだけで処理すると、バイト配列と文字コードの変換を自分で書く必要があります。

C#
byte[] bytes = Encoding.UTF8.GetBytes("テキスト");

一方、StreamReaderStreamWriterを使うと、文字列として扱いやすくなります。

C#
using StreamReader reader = new StreamReader("sample.txt", Encoding.UTF8);
string text = reader.ReadToEnd();

初心者がテキストファイルを扱うなら、まずStreamReaderStreamWriterを使い、バイナリ処理や細かい制御が必要になったらFileStreamを学ぶ流れでも問題ありません。

9-5. 非同期処理が必要な場合はReadAsync・WriteAsyncを使う

GUIアプリやWebアプリなどでは、ファイル操作中に処理を止めたくない場合があります。そのようなときは、ReadAsyncWriteAsyncを使います。ReadAsyncは非同期でバイト列を読み込み、WriteAsyncは非同期でバイト列を書き込むメソッドです。Microsoft Learn+1

C#
using System.IO;
using System.Text;

string path = "output.txt";
byte[] bytes = Encoding.UTF8.GetBytes("非同期で書き込みます。");

using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);

await fs.WriteAsync(bytes, 0, bytes.Length);

非同期処理を使うと、時間のかかるファイル操作でアプリケーションの応答性を保ちやすくなります。

10. C# FileStreamに関するよくある質問

10-1. FileStreamとFile.ReadAllTextはどちらを使うべき?

小さなテキストファイルを一括で読み込むだけなら、File.ReadAllTextが簡単です。

C#
string text = File.ReadAllText("sample.txt");

一方、大きなファイルを少しずつ読みたい場合、バイナリファイルを扱いたい場合、ファイル共有やアクセス権を細かく指定したい場合は、FileStreamが向いています。

初心者は、簡単なテキスト処理ならFile.ReadAllText、細かい制御が必要ならFileStreamと覚えるとよいでしょう。

10-2. FileStreamを閉じ忘れるとどうなる?

FileStreamを閉じ忘れると、ファイルが使用中のままになったり、他の処理から開けなくなったり、書き込み内容が反映されなかったりする可能性があります。

そのため、FileStreamは必ずusingで使いましょう。

C#
using FileStream fs = new FileStream("sample.txt", FileMode.Open, FileAccess.Read);

usingを使えば、例外が発生した場合でもリソースが解放されやすくなります。

10-3. FileStreamで追記するにはどうすればいい?

追記するには、FileMode.Appendを使います。

C#
using System.IO;
using System.Text;

string path = "log.txt";
string text = "追記する内容です。\n";

byte[] bytes = Encoding.UTF8.GetBytes(text);

using FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write);

fs.Write(bytes, 0, bytes.Length);

テキストを追記する場合は、StreamWriterを使うとさらに簡単です。

C#
using StreamWriter writer = new StreamWriter("log.txt", append: true, Encoding.UTF8);
writer.WriteLine("追記する内容です。");

10-4. FileStreamで日本語が文字化けする原因は?

主な原因は、文字コードの不一致です。

たとえば、ファイルがUTF-8で保存されているのに、別の文字コードとして読み込むと文字化けすることがあります。日本語を扱う場合は、保存時と読み込み時の文字コードを統一しましょう。

C#
byte[] bytes = Encoding.UTF8.GetBytes("こんにちは");
string text = Encoding.UTF8.GetString(bytes);

StreamReaderStreamWriterを使う場合も、Encoding.UTF8を指定すると分かりやすいです。

C#
using StreamReader reader = new StreamReader("sample.txt", Encoding.UTF8);

10-5. 初心者はFileStreamとStreamReader・StreamWriterのどちらから覚えるべき?

テキストファイルを扱うだけなら、初心者はStreamReaderStreamWriterから覚えると理解しやすいです。

C#
using StreamReader reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();

ただし、C#のファイル操作をしっかり理解するには、FileStreamも重要です。FileStreamを学ぶと、ファイルが内部的にはバイト列として扱われていることや、文字コード、バッファ、ファイル共有、リソース解放の考え方が理解しやすくなります。

まずはStreamReaderStreamWriterでテキストファイルを扱い、その後にFileStreamでバイト単位の読み書きを学ぶ流れがおすすめです。

まとめ

C#のFileStreamは、ファイルをバイト単位で読み書きするための基本的なクラスです。テキストファイルだけでなく、画像やPDFなどのバイナリファイルも扱えるため、C#で本格的なファイル操作を行ううえで重要な知識です。

FileStreamを使うときは、FileModeでファイルの開き方を指定し、FileAccessで読み込みや書き込みの権限を指定し、必要に応じてFileShareで他の処理との共有方法を指定します。

読み込みではRead、書き込みではWriteを使います。テキストを扱う場合は、Encoding.UTF8などの文字コードを指定して、バイト配列と文字列を正しく変換することが大切です。

また、FileStreamは使い終わったら必ず閉じる必要があります。閉じ忘れを防ぐために、基本的にはusingまたはusing varを使いましょう。

初心者のうちは、テキストファイルにはStreamReaderStreamWriterを組み合わせ、バイナリファイルや大容量ファイル、細かい制御が必要な場面ではFileStreamを使うと理解しやすくなります。