バイナリ・ファイルを読み書きするには?[C#、VB]:.NET TIPS
バイナリ・ファイル(=テキスト・ファイル以外のファイル)を読み書きするには、FileStreamクラスとSeek/Read/Writeの各メソッドを利用する。C#およびVBでの使い方を解説する。
バイナリ・ファイル(=テキスト・ファイル以外のファイル)を読み書きするには、FileStreamクラス(System.IO名前空間)を利用する。
FileStreamクラスはファイル用のストリームをサポートするクラスであり、Streamクラス(System.IO名前空間)の派生クラスの1つだ*1。基本的には、コンストラクタで指定したファイルのストリームに対して、Seekメソッドにより読み書きを行う位置決めを行い、Readメソッド/Writeメソッドで読み書きを行う。
*1 Streamクラスの派生クラスには、ほかに、
- NetworkStreamクラス(System.Net.Sockets名前空間)
- MemoryStreamクラス(System.IO名前空間)
- BufferedStreamクラス(System.IO名前空間)
- GZipStreamクラス(System.IO.Compression名前空間)
- CryptoStreamクラス(System.Security.Cryptography名前空間)
などがある。
ReadメソッドとWriteメソッド
FileStreamクラスのReadメソッドでは、パラメータとして、読み込んだデータを格納するためのbyte配列、読み込んだデータを書き込む(配列内の)位置、読み込むバイト数を指定する。
ファイルのどの位置のデータを読み込むかはReadメソッドでは指定しない。読み込みの開始位置はFileStreamオブジェクトによりストリームの「現在位置」として管理されており、これはReadメソッドを呼び出すたびに、読み込んだ分だけ先に進む。つまり、繰り返しReadメソッドを呼び出すことにより、順次データを読み込んでいけるというわけだ。
ファイルの任意の位置のデータを読み込むには、あらかじめSeekメソッドによりストリームの現在位置を移動させる。Seekメソッドの第1パラメータでは、現在位置を示すオフセットをlong型の値で指定する。第2パラメータでは、それが先頭あるいは末尾からのオフセットであるか、現在位置からのオフセットであるかをSeekOrigin列挙体(System.IO名前空間)の値により指定する。
Readメソッドの戻り値は、実際に読み込んだバイト数となる。ファイルの末尾付近では、この値は第3パラメータで指定した値(読み込むバイト数)よりも小さくなる可能性がある。また、ストリームの現在位置がファイルの末尾に達している場合には、Readメソッドの戻り値は0となる。
次のコードは、バイナリ・ファイル「test.bin」をオープンし、byte配列に1024bytesずつ読み込むサンプル・プログラムである。FileStreamクラスでは、Lengthプロパティによりオープンしているファイルのサイズを取得できる。
// binread.cs
using System;
using System.IO;
class BinaryRead {
static void Main(string[] args) {
FileStream fs = new FileStream(
@"test.bin", FileMode.Open, FileAccess.Read);
int fileSize = (int)fs.Length; // ファイルのサイズ
byte[] buf = new byte[fileSize]; // データ格納用配列
int readSize; // Readメソッドで読み込んだバイト数
int remain = fileSize; // 読み込むべき残りのバイト数
int bufPos = 0; // データ格納用配列内の追加位置
while (remain > 0) {
// 1024Bytesずつ読み込む
readSize = fs.Read(buf, bufPos, Math.Min(1024, remain));
bufPos += readSize;
remain -= readSize;
}
fs.Dispose();
}
}
// コンパイル方法:csc binread.cs
binread.csのダウンロード
' binread.vb
Imports System
Imports System.IO
Class BinaryRead
Shared Sub Main(ByVal args As String())
Dim fs As New FileStream( _
"test.bin", FileMode.Open, FileAccess.Read)
Dim fileSize As Integer = CInt(fs.Length) ' ファイルのサイズ
Dim buf(fileSize - 1) As Byte ' データ格納用配列
Dim readSize As Integer ' Readメソッドで読み込んだバイト数
Dim remain As Integer = fileSize ' 読み込むべき残りのバイト数
Dim bufPos As Integer = 0 ' データ格納用配列内の追加位置
While remain > 0
' 1024Bytesずつ読み込む
readSize = fs.Read(buf, bufPos, Math.Min(1024, remain))
bufPos += readSize
remain -= readSize
End While
End Sub
End Class
' コンパイル方法:vbc binread.vb
binread.vbのダウンロード
ここでは記述例を示すために1024bytesずつファイルを読み込んでいるが、(読み込むファイルのサイズが巨大でなければ)次のようにして全体を一度に読み込んでもよい。
readSize = fs.Read(buf, 0, fs.Length)
一方、バイナリ・データ(byte配列)をファイルに書き込むには、Writeメソッドを使用する。Writeメソッドのパラメータには、データが格納されているbyte配列、書き込むデータの配列内での位置、書き込むバイト数を指定する。Readメソッドと同様に、書き込む位置はストリームの現在位置となる(Writeメソッドと上述したSeekメソッドのコード例については、次のサンプル・プログラムを参照)。
読み込みエラーを無視してコピーを行うサンプル・プログラム
次のコードは、Read、Write、Seekメソッドを使用したサンプル・プログラムである。このプログラムでは、読み込み時のエラーを無視してコピーを行う(読み取れなかった部分は0で埋める)。
傷の付いたDVDなどを読もうとしたら、CRCエラーが出てしまったが、一部欠損していてもよいので、とにかくファイルをコピーしたいといった場合に利用できるプログラムだ。
// bincopy.cs
using System;
using System.IO;
class BinaryCopy {
static void Main(string[] args) {
string srcName = args[0]; // コピー元のファイル名
string destName = args[1]; // コピー先のファイル名
const int BUFSIZE = 2048; // 1度に処理するサイズ
byte[] buf = new byte[BUFSIZE]; // 読み込み用バッファ
byte[] ZEROARRAY = new byte[BUFSIZE]; // 0埋め用
int readSize; // Readメソッドで読み込んだバイト数
using (FileStream src = new FileStream(
srcName, FileMode.Open, FileAccess.Read))
using (FileStream dest = new FileStream(
destName, FileMode.Create, FileAccess.Write)) {
while (true) {
try {
readSize = src.Read(buf, 0, BUFSIZE); // 読み込み
} catch {
// 読み込みに失敗した場合
Console.WriteLine("read error at " + src.Position);
if (src.Length - src.Position < BUFSIZE) {
readSize = (int)(src.Length - src.Position);
} else {
readSize = BUFSIZE;
}
src.Seek(readSize, SeekOrigin.Current);
dest.Write(ZEROARRAY, 0, readSize); // 0埋めで書き込み
continue;
}
if (readSize == 0) {
break; // コピー完了
}
dest.Write(buf, 0, readSize); // 書き込み
}
}
}
}
// コンパイル方法:csc bincopy.cs
bincopy.csのダウンロード
' bincopy.vb
Imports System
Imports System.IO
Class BinaryCopy
Shared Sub main(ByVal args As String())
Dim srcName As String = args(0) ' コピー元のファイル名
Dim destName As String = args(1) ' コピー先のファイル名
Dim BUFSIZE As Integer = 2048 ' 1度に処理するサイズ
Dim buf(BUFSIZE) As Byte ' 読み込み用バッファ
Dim ZEROARRAY(BUFSIZE) As Byte ' 0埋め用
Dim readSize As Integer ' Readメソッドで読み込んだバイト数
Using src As New FileStream( _
srcName, FileMode.Open, FileAccess.Read)
Using dest As New FileStream( _
destName, FileMode.Create, FileAccess.Write)
While True
Try
readSize = src.Read(buf, 0, BUFSIZE) ' 読み込み
Catch
' 読み込みに失敗した場合
Console.WriteLine("read error at " & src.Position)
If src.Length - src.Position < BUFSIZE Then
readSize = CLng(src.Length - src.Position)
Else
readSize = BUFSIZE
End If
src.Seek(readSize, SeekOrigin.Current)
dest.Write(ZEROARRAY, 0, readSize) ' 0埋めで書き込み
Continue While
End Try
If readSize = 0 Then
Exit While ' コピー完了
End If
dest.Write(buf, 0, readSize) ' 書き込み
End While
End Using
End Using
End Sub
End Class
' コンパイル方法:vbc bincopy.vb
bincopy.vbのダウンロード
プログラムでは、Readメソッドが失敗したときには(この場合には例外が発生する)、読み込み用のストリームをSeekメソッドにより進め、書き込み用のストリームには0を書き込む。FileStreamクラスのPositionプロパティはストリームの現在位置を表す。
なお、SeekメソッドはFileStreamオブジェクトが内部で管理する現在位置を移動するだけで、ファイルの読み込めない部分に現在位置を移動したとしても呼び出しは失敗しない。実際にエラーが発生するのは、その後にReadメソッドを呼び出した場合である。
利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:クラス・ライブラリ 処理対象:バイナリ・ファイル
使用ライブラリ:FileStreamクラス(System.IO名前空間)
使用ライブラリ:SeekOrigin列挙体(System.IO名前空間)
Copyright© Digital Advantage Corp. All Rights Reserved.