FileStreamクラス

ストリームを利用したファイルの読み書き

ファイルの読み書きはテキストファイルの読み書きバイナリファイルの読み書きで説明したFileクラスのメソッドを使用するのが簡単な方法です。
しかしこれらはあまり細かい制御はできないので、その場合はFileStreamクラスを利用します。

Streamに関してはStreamを参照してください。
このページの解説は上記ページを読んでいることが前提となっています。

ファイルを開く

ファイルを開くにはnewキーワードでFileStreamクラスのインスタンスを生成します。
以下はファイルを開いて閉じるだけのコードですが、実行すると指定のパスに空のファイルが生成されます。
ファイルがすでに存在する場合は上書きされるので注意してください。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//↓追加しておく
using System.IO

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = "test.bin";

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

            //読み書き処理...

            fs.Close();

            Console.WriteLine("プログラム終了");
            Console.ReadLine();
        }
    }
}

このページではこれ以降usingステートメントを利用したコードで記述します。


using (FileStream fs = new FileStream(
    path, FileMode.Create, FileAccess.ReadWrite))
{
    //読み書き処理...
}

コンストラクター

コンストラクターの第一引数には開きたいファイルのパスを指定します。
第二引数はファイルのオープンモードFileMode列挙型で指定します。
FileMode列挙型の値は以下です。

名前 説明 読込 書込 ファイルが存在 ファイルが無い
Append ファイルの末尾にデータを追加する。
末尾よりも前にシークはできない。
(IOException)
不可 必須 追記用に開く 新規作成
Create 新しいファイルを作成する。 必須 データを消去して開く 新規作成
CreateNew 新しいファイルを作成する。 必須 例外(IOException) 新規作成
Truncate 新しいファイルを作成する。 必須 データを消去して開く 例外(FileNotFoundException)
Open ファイルを開く。 開く 例外(FileNotFoundException)
OpenOrCreate ファイルを開く。 開く 新規作成

同じような動作をするモードがありますが、指定のファイルがすでに存在する場合/しない場合の動作が異なります。

第三引数にはFileAccess列挙型を指定しますが、これは省略可能です。
FileAccess列挙型の値は以下です。

Read
読み込み専用で開く
Write
書き込み専用で開く
ReadWrite
読み書き両用で開く

FileMode列挙型で書き込みが「必須」となっているものは、FileAccess列挙型で「Read」を指定すると例外(ArgumentException)が発生します。
「FileMode.Append」は読み込み不可なので、FileAccess列挙型は必ず「Write」を指定する必要があります。
「FileMode.Create」は読み込み可/書き込み必須なので、「Write」「ReadWrite」を指定できます。
「FileMode.Open」は特に制限がないので、FileAccessは自由に指定できます。

FileAccess列挙型の指定を省略した場合、第二引数が「FileMode.Append」ならば「FileAccess.Write」で、それ以外ならば「FileAccess.ReadWrite」で開かれます。

Fileクラスを利用したオープン

FileStreamのインスタンスはFileクラスのメソッドでも生成できます。


string path = "test.bin";

//FileMode.Open, FileAccess.Read
using (FileStream fs = File.OpenRead(path))
{
}

//FileMode.Create, FileAccess.Write
using (FileStream fs = File.OpenWrite(path))
{
}

using (FileStream fs = File.Open(path, FileMode.CreateNew))
{
}

using (FileStream fs = File.Open(path, FileMode.CreateNew, FileAccess.Write))
{
}

File.OpenRead、File.OpenWriteはFileModeとFileAccessが固定です。
File.Openはnewキーワードで生成する場合と同じ動作となります。

書き込み

ファイルへのデータの書き込みはWriteメソッドで行います。


string path = "test.bin";

byte[] bytesNum = BitConverter.GetBytes(12345);
byte[] bytesStr = Encoding.Unicode.GetBytes("あいうえお");

using (FileStream fs = new FileStream(path, FileMode.Create))
{
    //配列全体を書き込み
    fs.Write(bytesNum, 0, bytesNum.Length);

    //配列の2番目の要素から6つ分を書き込み
    //(いうえ)
    fs.Write(bytesStr, 2, 6);
}

Writeメソッドはbyte型配列をファイルに書き込みます。
第一引数はストリームに書き込む配列を指定します。
第二引数は配列のコピー開始位置を指定します。
第三引数は書き込むバイト数を指定します。

WriteByte

WriteByteメソッドはストリームに1バイトのデータを書き込みます。


string path = "test.bin";

byte[] bytesNum = BitConverter.GetBytes(12345);
byte[] bytesStr = Encoding.Unicode.GetBytes("あいうえお");

using (FileStream fs = new FileStream(path, FileMode.Create))
{
    foreach (var b in bytesNum)
        fs.WriteByte(b);

    for(int i = 2; i < 8; i++)
        fs.WriteByte(bytesStr[i]);
}

Flush

ファイルに実際にデータが書き込まれるのは、ストリームのClose(Dispose)メソッドを呼び出した時です。
(usingステートメントを使用している場合はusingブロックを抜けたとき)
ストリームを閉じずにデータを反映したい場合はFlushメソッドを呼び出します。


string path = "test.bin";

byte[] bytesNum = BitConverter.GetBytes(12345);
byte[] bytesStr = Encoding.Unicode.GetBytes("あいうえお");

using (FileStream fs = new FileStream(path, FileMode.Create))
{//この時点でファイルは生成される

    foreach (var b in bytesNum)
        fs.WriteByte(b);

    fs.Flush(); //bytesNumのデータがファイルに反映される

    for(int i = 2; i < 8; i++)
        fs.WriteByte(bytesStr[i]);
}//すべてのデータがファイルに反映される

読み込み

ファイルからデータを読み込むにはReadメソッドを使用します。
ここでは先ほど出力した「test.bin」ファイルを読み込んでみます。


string path = "test.bin";

byte[] bytesNum = BitConverter.GetBytes(0);
byte[] bytesStr = Encoding.Unicode.GetBytes("abcde");

using (FileStream fs = new FileStream(path, FileMode.Open))
{
    //配列全体に格納
    //(4バイト読み取り)
    fs.Read(bytesNum, 0, bytesNum.Length);

    //配列の2番目の要素から6つ分に格納
    //(6バイト読み取り)
    fs.Read(bytesStr, 2, 6);
}

Console.WriteLine(BitConverter.ToInt32(bytesNum, 0));
Console.WriteLine(Encoding.Unicode.GetString(bytesStr));

Console.ReadLine();
12345
aいうえe

Readメソッドはストリームから指定のバイト数を読み取り配列に格納します。
データ型などは考慮しないので、Writeメソッドで書き込んだ際と同じ順番、同じサイズでデータを読み取ることで元のデータに復元できます。

戻り値は読み取ったバイト数をint型で返します。
基本的には第三引数の値と一致しますが、ストリームの残りのデータが指定のバイト数に満たない場合は少ない値が返ってきます。
読み取れなかった場合は0が返りますので、ストリームの末尾と判断できます。


string path = "test.bin";

byte[] buf = new byte[3];

using (FileStream fs = new FileStream(path, FileMode.Open))
{
    int read;
    while((read = fs.Read(buf, 0, buf.Length)) > 0)
    {
        for(int i = 0; i < read; i++)
            Console.Write("{0:X2} ", buf[i]);
    }
}
Console.ReadLine();
39 30 00 00 40 30 46 30 48 30 

ReadByte

ReadByteメソッドはストリームから1バイトを読み取ります。


string path = "test.bin";

using (FileStream fs = new FileStream(path, FileMode.Open))
{
    int read;
    while ((read = fs.ReadByte()) >= 0)
        Console.Write("{0:X2} ", (byte)read);
}

ReadByteメソッドは読み取ったデータをint型で返しますので必要に応じてキャストします。
ストリームの末尾に達した場合は-1を返します。