ストリーム

クラス階層

  • System.Object
    • System.IO.File
    • System.IO.Directory
    • System.IO.Path
    • System.IO.BinaryReader … バイナリの読み取り (バイナリと基本型を変換)
    • System.IO.BinaryWriter
    • System.MarshalByRefObject
      • System.IO.Stream
        • System.IO.FileStream … バイト配列の読み書き
        • System.IO.MemoryStream
        • System.IO.Pipes.PipeStream
        • System.IO.UnmanagedMemoryStream
      • System.IO.FileSystemInfo
        • System.IO.FileInfo
        • System.IO.DirectoryInfo
      • System.IO.TextReader
        • System.IO.StreamReader … 文字列の読み取り (バイトと文字を変換)
        • System.IO.StringReader … (文字と文字列を変換)
      • System.IO.TextWriter
ストリーム - ファイルおよびストリーム入出力 - .NET | Microsoft Docs

バイト配列

FileStream

次のようにすると、”ABC”という文字列を含む"sample.txt"というファイルが作成されます。

string path = "sample.txt";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
    byte[] array = new byte[] { 65, 66, 67 }; // ABC
    fs.Write(array, 0, array.Length);
} // ここでストリームが閉じられる

ストリームは閉じられるときに書き込まれますので、usingなどで明示的にリソースを解放します。

コンストラクタ

public FileStream(
    string path,
    FileMode mode
)
FileStream コンストラクター (String, FileMode) (System.IO) | MSDN

FileStreamのインスタンスは、次のようにFile.Create()メソッドでも作成できます。

FileStream fs = File.Create(path)
File.Create メソッド (String) (System.IO) | MSDN

メソッド

メソッド  
CopyTo(Stream) 現在のストリームから、別のストリームへ書き込む
Read(Byte[], Int32, Int32) 指定位置から指定バイト数だけ読み取り、指定のバッファへ書き込む
ReadByte() 現在位置から1バイト読み取り、それを返す
Write(Byte[], Int32, Int32)  
WriteByte(Byte)  
非同期のメソッド (すべてStreamからの継承)
メソッド  
CopyToAsync(Stream)  
ReadAsync(Byte[], Int32, Int32)  
WriteAsync(Byte[], Int32, Int32)  
メソッド - FileStream クラス (System.IO) | MSDN
読み込み
同期
public override int Read(
    byte[] array,
    int offset,
    int count
)
FileStream.Read メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
非同期
[ComVisibleAttribute(false)]
[HostProtectionAttribute(SecurityAction.LinkDemand, ExternalThreading = true)]
public Task<int> ReadAsync(
    byte[] buffer,
    int offset,
    int count
)
Stream.ReadAsync メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
書き込み
同期
public override void Write(
    byte[] array,
    int offset,
    int count
)
FileStream.Write メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
非同期
[ComVisibleAttribute(false)]
[HostProtectionAttribute(SecurityAction.LinkDemand, ExternalThreading = true)]
public Task WriteAsync(
    byte[] buffer,
    int offset,
    int count
)
Stream.WriteAsync メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
public async void Method()
{
    // await式に到達するまでは、同期的に実行される

    using (FileStream fs = new FileStream("sample.txt", FileMode.Create))
    {
        byte[] array = new byte[1000 * 1000];
        await fs.WriteAsync(array, 0, array.Length); // ここで呼び出し元へ制御が戻る

        // 書き込みが完了すると、これ以降が処理される
    }
}

awaitで待たせないと、書き込み完了前にストリームが閉じられることがあります。ファイル アクセスにおける非同期の使用 (C#) | Microsoft Docs

バイト以外の配列

配列がbyte型ならば、Write(Byte[])などのバイト配列向けのメソッドだけで処理できます。しかしそれ以外の型ならば、要素を1つずつ処理します。

int[] items = new int[] { 1, 2, 3 };

// 書き込み
using (FileStream fileStream = File.Create("sample.dat"))
using (BinaryWriter binaryWriter = new BinaryWriter(fileStream))
{
    foreach (int item in items)
    {
        binaryWriter.Write(item);
    }
}

// 読み取り
int[] result;

using (FileStream fileStream = File.OpenRead("sample.dat"))
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
    long length = binaryReader.BaseStream.Length / sizeof(int);
    result = new int[length];

    for (int i = 0; i < length; i++)
    {
        result[i] = binaryReader.ReadInt32();
    }
}

バイナリ

BinaryWriter

// 書き込み
using (FileStream fileStream = File.Open("sample.dat", FileMode.Create))
using (BinaryWriter binaryWriter = new BinaryWriter(fileStream))
{
    binaryWriter.Write(10);   // 0x0a 0x00 0x00 0x00
    binaryWriter.Write(1.0f); // 0x00 0x00 0x80 0x3F
    binaryWriter.Write(true); // 0x01
    binaryWriter.Write("ab"); // 0x02 0x61 0x62
}
BinaryWriter クラス (System.IO) | MSDN

BinaryReader

// 読み取り
using (FileStream fileStream = File.Open("sample.dat", FileMode.Open))
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
    int    a1 = binaryReader.ReadInt32();   // 10
    float  a2 = binaryReader.ReadSingle();  // 1
    bool   a3 = binaryReader.ReadBoolean(); // true
    string a4 = binaryReader.ReadString();  // "ab"

    // すべてのデータの読み取り
    byte[] result = new byte[binaryReader.BaseStream.Length];
    binaryReader.Read(result, 0, result.Length);
}
BinaryReader クラス (System.IO) | MSDN

ファイルの末尾までの読み取り

BaseStreamプロパティからStreamを取得し、そのPositionがLengthより小さいことで末尾に達していないことを確認できます。binaryfiles - C# checking for binary reader end of file - Stack Overflow

Stream stream = binaryReader.BaseStream;
while(stream.Position < stream.Length)
{
    // 読み取り処理
}

メモリ

byte[] src = new byte[] { 1, 2, 3 };
byte[] dest;

using (MemoryStream memoryStream = new MemoryStream())
{
    // 書き込む
    memoryStream.Write(src, 0, src.Length);

    // ストリームの位置を先頭へ戻す
    memoryStream.Seek(0, SeekOrigin.Begin);

    // 読み込む
    dest = new byte[memoryStream.Length];
    int count = memoryStream.Read(dest, 0, dest.Length);
}

アンマネージド メモリ

[System.CLSCompliant(false)]
[System.Security.SecurityCritical]
public UnmanagedMemoryStream (
    byte* pointer,              // アンマネージド メモリへのポインタ
    long length,                // 使用するメモリの長さ
    long capacity,              // ストリームに割り当てられたメモリの総量
    System.IO.FileAccess access // FileAccessの値
    );
UnmanagedMemoryStream(Byte*, Int64, Int64, FileAccess) - UnmanagedMemoryStream Constructor (System.IO) | Microsoft Docs
byte[] array = { 1, 2, 3 };

int size = Marshal.SizeOf(array[0]) * array.Length;
IntPtr intPtr = Marshal.AllocHGlobal(size);

unsafe
{
    // IntPtrからbyteポインタを取得する
    byte* bytePtr = (byte*)intPtr.ToPointer();

    // アンマネージド メモリへのポインタを使用するUnmanagedMemoryStreamオブジェクトを作成する
    using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(bytePtr, array.Length, array.Length, FileAccess.Write))
    {
        // データを書き込む
        stream.Write(array, 0, array.Length);

        // ストリームを閉じる
        stream.Close();
    }
}

Marshal.FreeHGlobal(intPtr);

テキスト

StreamWriter

コンストラクタ

public StreamWriter(
    string path
)
StreamWriter コンストラクター (String) (System.IO) | MSDN

ファイルが存在する場合には上書きされます。これを追記するようにするには、次の形式でappendをtrueとします。

public StreamWriter(
    string path,
    bool append // 追記するかどうか
)
StreamWriter コンストラクター (String, Boolean) (System.IO) | MSDN

既定では文字エンコーディングはUTF-8となります。これを変更するには、encodingの指定を追加します。

public StreamWriter(
    string path,
    bool append,      // 追記するかどうか
    Encoding encoding // 文字エンコーディング
)
StreamWriter コンストラクター (String, Boolean, Encoding) (System.IO) | MSDN
string path = "sample.txt";
using (StreamWriter sw = new StreamWriter(path))
{
    sw.Write("ABC");
}

メソッド

メソッド  
   
メソッド - StreamWriter クラス (System.IO) | MSDN
public override void Write(
    string value
)
StreamWriter.Write メソッド (String) (System.IO) | MSDN

StreamReader

  • System.Object
    • System.MarshalByRefObject
      • System.IO.TextReader
        • System.IO.StreamReader
StreamReader クラス (System.IO) | MSDN

コンストラクタ

public StreamReader(
    string path
)
StreamReader コンストラクター (String) (System.IO) | MSDN

プロパティ

プロパティ  
Encoding CurrentEncoding 現在の文字エンコーディング
bool EndOfStream ストリームの末尾に達しているならば、true
プロパティ - StreamReader クラス (System.IO) | MSDN

メソッド

戻り値の型 メソッド 読み込み基準 読み込み範囲 末尾から読み込んだときの戻り値
int Read() ストリームの現在位置 1文字 -1
int Read(Char[], Int32, Int32) ストリームの指定位置 指定文字数 0
int ReadBlock(Char[], Int32, Int32) ストリームの指定位置 指定文字数 0
string ReadLine() ストリームの現在位置 1行 null
string ReadToEnd() ストリームの現在位置 末尾まで ""
メソッド - StreamReader クラス (System.IO) | MSDN
using (StreamReader sr = new StreamReader("samle.txt"))
{
    string text = sr.ReadToEnd();
}
c# - Difference between StreamReader.Read and StreamReader.ReadBlock - Stack Overflow

StringReader

StringReaderは文字列からしか初期化できず、ストリームの処理に用いることはできません。

public StringReader(
    string s
)
StringReader コンストラクター (String) (System.IO) | MSDN

CSV

書き込み

書き込み用のクラスなどは用意されていないため、自前で処理します。c# - Best practices for serializing objects to a custom string format for use in an output file - Stack Overflow

定義に従うならばエスケープの処理などが必要ですが、簡易で良ければString.Join()でカンマで連結し、StreamWriterのWriteLine()でファイルに書き込めます。

string[][] data = new string[][]
{
    new string[]{ "a", "b", "c" },
    new string[]{ "d", "e" }
};

using (StreamWriter streamWriter = new StreamWriter("sample.csv"))
{
    foreach (string[] item in data)
    {
        streamWriter.WriteLine(String.Join(",", item));
    }
}

読み込み (TextFieldParser)

TextFieldParserクラスで読み込めます。

Microsoft.VisualBasic.FileIO.TextFieldParser parser
    = new Microsoft.VisualBasic.FileIO.TextFieldParser("sample.csv");

// 区切り文字を設定する。これは既定ではnullとなっており、設定しなければ読み込み時に例外が発生する
parser.SetDelimiters(new string[] { "," });

List<List<string>> list = new List<List<string>>();
while (!parser.EndOfData)
{
    // 現在の行から、指定形式でフィールドを読み込む
    string[] fields = parser.ReadFields();
    list.Add(new List<string>(fields));
}

// 結果を配列として得たいならば、リストを配列に変換する
string[][] array = list.Select(a => a.ToArray()).ToArray();

TextFieldParserの利用時に「型または名前空間の名前 'FileIO' が名前空間 'Microsoft.VisualBasic' に存在しません」としてエラーとなるときには、Microsoft.VisualBasicを参照に追加します。

Delimitersにカンマ以外を設定すれば、CSV以外の形式にも対応できます。またTextFieldTypeプロパティをFixedWidthとしてFieldWidthsでその幅を指定すると、固定幅でも読み込めます。

FieldType 列挙型
メンバー フィールドの形式
Delimited 区切り文字で分割
FixedWidth 固定幅で分割
FieldType 列挙型 (Microsoft.VisualBasic.FileIO) | MSDN

ReadFields()

public string[] ReadFields()
TextFieldParser.ReadFields メソッド (Microsoft.VisualBasic.FileIO) | MSDN
  • 空行はすべて飛ばされ、空行ではない最初の行の解析結果が返される。そしてカーソルは、空行ではない行まで進む。
  • 解析に失敗した場合は例外が発生し、カーソルは進まない。

XML

書き込み

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;

using (XmlWriter writer = XmlWriter.Create("sample.xml", settings))
{
    writer.WriteComment("SAMPLE");
    writer.WriteStartElement("root");

    writer.WriteStartElement("e1");
    writer.WriteAttributeString("a1", "10");

    writer.WriteStartElement("e2");
    writer.WriteAttributeString("a2", "20");
    writer.WriteString("ABC");
    writer.WriteEndElement();

    writer.WriteStartElement("e2");
    writer.WriteAttributeString("a3", "30");
    writer.WriteString("DEF");
    writer.WriteEndElement();

    writer.WriteEndElement();
    writer.WriteEndElement();
}

このコードは次のようなファイルを出力します。

<?xml version="1.0" encoding="utf-8"?>
<!--SAMPLE-->
<root>
  <e1 a1="10">
    <e2 a2="20">ABC</e2>
    <e2 a3="30">DEF</e2>
  </e1>
</root>

最後にClose()を呼ぶ必要がありますが、これはDispose()でも呼ばれます。

読み込み

using (XmlReader reader = XmlReader.Create("sample.xml"))
{
    reader.MoveToContent();
    Console.WriteLine(reader.Name); // "root"

    reader.ReadToFollowing("e2");
    Console.WriteLine(reader.GetAttribute("a2")); // "20"
    Console.WriteLine(reader.ReadElementContentAsString()); // "ABC"

    XmlNodeType type1 = reader.NodeType; // Whitespace
    reader.Skip();
    XmlNodeType type2 = reader.NodeType; // Element

    Console.WriteLine(reader.GetAttribute("a3")); // "30"
    Console.WriteLine(reader.ReadElementContentAsString()); // "DEF"
}

参考

参考書

MSDN (Microsoft Developer Network) から検索