BinaryReader/Writerクラス
Streamクラスとバイナリ
Streamクラスを利用したデータの読み書きは、データをbyte型の配列(バイナリ)で扱います。
そのままではやや扱いづらいので、BinaryReader、BinaryWriterクラスを利用した読み書き方法が提供されています。
BinaryReader/WriterクラスはStream(の派生)クラスと組み合わせて使用します。
まずStreamのインスタンスを生成し、それをBinaryReader/Writerのコンストラクターに渡します。
これでBinaryReader/WriterはStreamとの間に入ってデータを適切に加工してくれます。
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
//brを通してmsの読み書き
bw.Write(123);
bw.Close();
ms.Close();
このページではusingステートメントを利用したコードで記述します。
using (MemoryStream ms = new MemoryStream())
using (BinaryReader br = new BinaryReader(ms))
{
//読み書き処理...
}
BaseStreamプロパティ
BinaryReader/WriterのインスタンスはBaseStreamプロパティで操作対象のストリームを取得することができます。
using (MemoryStream ms = new MemoryStream())
using (BinaryReader br = new BinaryReader(ms))
{
//BaseStreamプロパティはMemoryStreamを返すので
//MemoryStreamのメソッドを使用可能
br.BaseStream.WriteByte((byte)1);
}
BinaryWriterクラス
データの書き込みにはBinaryWriterクラスのWriteメソッドを使用します。
string path = "test.bin";
byte[] bytes = new byte[] { 1, 2, 3, 4, 5 };
char[] chars = new char[] { 'a', 'b', 'c', 'd', 'e' };
using (FileStream fs = new FileStream(path, FileMode.Create))
using (BinaryWriter bw = new BinaryWriter(fs))
{
bw.Write(123); //int型
bw.Write(4.56); //double型
bw.Write(true); //bool型
bw.Write("あいうえお"); //string型
//byte型配列をすべて書き込み
bw.Write(bytes);
//配列の2番目の要素から3つ分を書き込み
bw.Write(bytes, 2, 3);
//char型配列をすべて書き込み
bw.Write(chars);
//配列の2番目の要素から3つ分を書き込み
bw.Write(chars, 2, 3);
}
Writeメソッドはobject型以外の組み込み型をそのまま書き込むことができます。
その他、byte型配列とchar型配列を書き込むこともできます。
Encoding
文字や文字列を書き込む際、文字コードはデフォルトでUTF-8で書き込まれます。
文字コードを変更する場合はコンストラクターでEncodingを指定します。
(System.Text.Encoding)
//UTF-16
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms, Encoding.Unicode))
{
}
//Shift-JIS
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms, Encoding.GetEncoding("shift-jis")))
{
}
詳しくはEncodingクラスを参照してください。
シーク
シークを行うにはSeekメソッドを使用するほか、BaseStreamを経由してSeekメソッドを実行あるいはPositionプロパティを操作します。
using (MemoryStream ms = new MemoryStream())
using (BinaryWriter bw = new BinaryWriter(ms))
{
for (int i = 0; i < 255; i++)
bw.Write((byte)i);
bw.Seek(0, SeekOrigin.Begin);
bw.BaseStream.Seek(0, SeekOrigin.End);
bw.BaseStream.Position = 0;
}
BinaryReaderクラス
データの読み込みにはBinaryReaderクラスを使用します。
ここでは先ほど作成したファイル「test.bin」を読み込んでみます。
string path = "test.bin";
using (FileStream fs = new FileStream(path, FileMode.Open))
using (BinaryReader br = new BinaryReader(fs))
{
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadDouble());
Console.WriteLine(br.ReadBoolean());
Console.WriteLine(br.ReadString());
foreach (var b in br.ReadBytes(5))
Console.Write("{0} ", b);
Console.WriteLine();
foreach (var b in br.ReadBytes(3))
Console.Write("{0} ", b);
Console.WriteLine();
foreach (var c in br.ReadChars(5))
Console.Write("{0} ", c);
Console.WriteLine();
foreach (var c in br.ReadChars(3))
Console.Write("{0} ", c);
Console.WriteLine();
}
123 4.56 True あいうえお 1 2 3 4 5 3 4 5 a b c d e c d e
BinaryReaderの読み取りメソッドには以下があります。
メソッド名 | 読み取るサイズ | 戻り値の型 |
---|---|---|
ReadByte | 1バイト | byte型 |
ReadSByte | 1バイト | sbyte型 |
ReadInt16 | 2バイト | short型 |
ReadUInt16 | 2バイト | ushort型 |
ReadInt32 | 4バイト | int型 |
ReadUInt32 | 4バイト | uint型 |
ReadInt64 | 8バイト | long型 |
ReadUInt64 | 8バイト | ulong型 |
ReadSingle | 4バイト | float型 |
ReadDouble | 8バイト | double型 |
ReadDecimal | 16バイト | decimal型 |
ReadBoolean | 4バイト | bool型 |
ReadChar | 1文字 | char型 |
ReadString | 任意 | string型 |
これらのメソッドはストリームの現在の位置から指定のバイト数分を読み取り、指定のデータ型に変換します。
現在の位置に期待通りのデータが存在しなければ正しいデータは得られません。
ReadCharメソッドは「1バイト」ではなく「1文字」を読み取ります。
1文字のバイト数は文字コードによって変わります。
ReadStringメソッドは文字列を読み取りますが、読み取れるのはBinaryWriterを利用して書き込んだ文字列に限ります。
BinaryWriterの文字列書き込みはデータの先頭に文字列の長さを格納しており、BinaryReaderはこれを利用して読み取る文字列の長さを判断しています。
BinaryWriterを経由せずに文字列を書き込んだ場合は長さが格納されていないため、ReadStringメソッドでは読み取ることはできません。
上記の表のメソッドは、ストリームの終端を超えてデータを読み取ろうとすると例外(EndOfStreamException)が発生します。
その他、ReadBytesメソッドでbyte型配列を、ReadCharsメソッドでchar型配列を読み取れます。
ReadBytesメソッドの引数は読み取るバイト数を指定します。
ReadCharsメソッドの引数は読み取る文字数を指定します。
この二つのメソッドはストリームの末尾を超えてデータを読み取ろうとするとストリームの残りのデータの配列が返されます。
例えばストリームの残りが5バイトのとき、「br.ReadBytes(10)」を実行すると要素数5のbyte型配列が返されます。
ストリームが終端の場合は要素数0の配列が返されます。
つまり例外は発生しません。
Read、PeekChar
データ読み取りメソッドにはReadメソッドとPeekCharメソッドも使用できます。
メソッド | 説明 | 戻り値 | ストリーム終端を超えた場合 |
---|---|---|---|
Read() | 1文字を読み取る 読み取ったデータが現在の文字列エンコードに変換できない場合は例外発生(ArgumentException) |
読み取った文字(int型) | -1を返す |
Read(byte[] buffer, int index, int count) | countバイトを読み取りbuffer[index]から順に格納 | 読み取れたバイト数(int型) | 残りのデータをbufferに格納し、読み取れたバイト数を返す すでに終端に位置する場合は0を返す |
Read(char[] buffer, int index, int count) | count文字分を読み取りbuffer[index]から順に格納 | 読み取れた文字数(int型) | 残りのデータをbufferに格納し、読み取れた文字数を返す すでに終端に位置する場合は0を返す |
PeekChar() | 1文字を読み取るが、ストリームの位置は移動させない 読み取ったデータが現在の文字列エンコードに変換できない場合は例外発生(ArgumentException) |
読み取った文字(int型) | -1を返す |
Encoding
文字や文字列を読み取る際、文字コードはデフォルトでUTF-8で変換されます。
文字コードを変更する場合はコンストラクターでEncodingを指定します。
これはBinaryWriterのEncodingの設定と同じです。
(System.Text.Encoding)
//UTF-16
using (var ms = new MemoryStream())
using (var br = new BinaryReader(ms, Encoding.Unicode))
{
}
//Shift-JIS
using (var ms = new MemoryStream())
using (var br = new BinaryReader(ms, Encoding.GetEncoding("shift-jis")))
{
}
読み込みと書き込みのEncodingは同じにする必要があります。
シーク
BinaryWriterにはSeekメソッドがありますが、BinaryReaderにはありません。
BinaryReaderでシークをする場合はBaseStreamを経由してシークします。
当然ながらストリームがシーク可能な場合に限ります。
using (MemoryStream ms = new MemoryStream())
using (BinaryReader br = new BinaryReader(ms))
{
if(br.BaseStream.CanSeek)
br.BaseStream.Position = 0;
}