ファイル操作 バイナリ ファイルを読み書きする バイナリファイル ( 即ちテキストファイル以外のファイル ) を読み書きするには FileStream クラス (System.IO 名前空間 ) を利用する FileStream クラスはファイル用のストリームをサポートするクラスで有り Stream クラス (System.IO 名前空間 ) の派生クラスの 1 つで有る 基本的には コンストラクタで指定したファイルのストリームに対して Seek メソッドに依り読み書きを行う位置を決め Read メソッドや Write メソッドで読み書きを行う 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 プロパティに依りオープンして居るファイルのサイズを取得出来る 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) -1-
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 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(); 此処では記述例を示す為に 1024bytes ずつファイルを読み込んで居るが ( 読み込むファイルのサイズが巨大でなければ ) 次の様にして全体を一度に読み込んでも良い readsize = fs.read(buf, 0, fs.length) 一方 バイナリデータ (byte 配列 ) をファイルに書き込むには Write メソッドを使用する Write メソッドのパラメータには データが格納されて居る byte 配列 書き込むデータの配列内での位置 書き込むバイト数を指定する Read メソッドと同様に 書き込む位置はストリームの現在位置と成る (Write メソッドと上述した Seek メソッドのコード例については 次のサンプルプログラムを参照 ) -2-
読み込みエラーを無視してコピーを行うサンプルプログラム 次のコードは Read Write Seek メソッドを使用したサンプルプログラムで有る 此のプログラムでは 読み込み時のエラーを無視してコピーを行う ( 読み取れなかった部分は 0 で埋める ) 傷の付いた DVD 等を読もうとしたら CRC エラーが出たが 一部欠損して居ても良いので 兎に角ファイルをコピーし度いと謂う場合に利用出来るプログラムで有る 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) End While End Using End Using End Sub End Class 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) ' 書き込み -3-
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); // 書き込み プログラムでは Read メソッドが失敗したときには ( 此の場合には例外が発生する ) 読み込み用のストリームを Seek メソッドに依り進め 書き込み用のストリームには 0 を書き込む FileStream クラスの Position プロパティはストリームの現在位置を表す なお Seek メソッドは FileStream オブジェクトが内部で管理する現在位置を移動するだけで ファイルの読み込めない部分に現在位置を移動したとしても呼び出しは失敗しない 実際にエラーが発生するのは その後に Read メソッドを呼び出した場合で有る -4-
バイナリ ファイルを簡単に読み書きする [2.0] 前述の バイナリファイルを読み書きする では FileStream クラス (System.IO 名前空間 ) を使用してバイナリファイルを読み書きする方法に付いて解説したが 単純にファイルの内容を総て byte 配列に読み込む 或いは逆に byte 配列全体をファイルに書き込むと謂う丈で有れば より簡単な方法が用意されて居る ( 但し.NET Framework 2.0 以降 ) 此れには File クラス (System.IO 名前空間 ) の ReadAllBytes メソッドや WriteAllBytes メソッドを使用する 此等の静的メソッドでは ファイルのオープン byte 配列への読み込みと byte 配列の書き込み ファイルのクローズと謂う処理を 1 回のメソッド呼び出しで実行可能で有る ReadAllBytes メソッドでは 読み込むファイルのパス名をパラメータに指定して呼び出すと 戻り値としてファイルの内容を読み込んだ byte 配列が返される Dim data() As Byte = File.ReadAllBytes("test.bin") ' 読み込み File.WriteAllBytes("testnew.bin", data) ' 書き込み byte[] data = File.ReadAllBytes("test.bin"); // 読み込み File.WriteAllBytes("testnew.bin", data); // 書き込み 亦 WriteAllBytes メソッドでは 書き込むファイルのパス名 ( 既にファイルが存在する場合には上書きされる ) と 書き込む byte 配列をパラメータで指定する 戻り値はない バイナリファイルを固定サイズで分割するサンプルプログラム 次のコードは ReadAllBytes メソッドと WriteAllBytes メソッドを使用したサンプルプログラムで有る 此のプログラムでは バイナリファイルを読み込み 其れを固定サイズ (1Mbytes) の複数ファイル ( ファイル名は out0001.bin out0002.bin ) に分割して出力する 猶 出力される最後のファイルは固定サイズに満たない場合が有る Imports System Imports System.IO Class BinarySplit Shared Sub Main() ' バイナリ ファイルの読み込み Dim src() As Byte = File.ReadAllBytes("test.bin") Dim FILESIZE As Integer = 1024 * 1000 ' 分割サイズ Dim num As Integer = 0 Dim remain As Integer = src.length While remain > 0 ' 作成する分割ファイルの実際のサイズ Dim length As Integer = Math.Min(FILESIZE, remain) -5-
' 分割ファイルへ書き出す byte 配列の作成 Dim dest(length - 1) As Byte Array.Copy(src, num * FILESIZE, dest, 0, length) ' 出力ファイル名 (out0001.bin out0002.bin ) Dim name As String = String.Format("out{0:D4.bin", num + 1) ' byte 配列のファイルへの書き込み File.WriteAllBytes(name, dest) num += 1 remain -= FILESIZE End While End Sub End Class using System; using System.IO; class BinarySplit { static void Main() { // バイナリ ファイルの読み込み byte[] src = File.ReadAllBytes("test.bin"); int FILESIZE = 1024 * 1000; // 分割サイズ int num = 0; for (int remain = src.length; remain > 0; remain -= FILESIZE) { // 作成する分割ファイルの実際のサイズ int length = Math.Min(FILESIZE, remain); // 分割ファイルへ書き出す byte 配列の作成 byte[] dest = new byte[length]; Array.Copy(src, num * FILESIZE, dest, 0, length); // 出力ファイル名 (out0001.bin out0002.bin ) string name = String.Format("out{0:D4.bin", num + 1); // byte 配列のファイルへの書き込み File.WriteAllBytes(name, dest); num++; ReadAllBytes メソッドはファイル全体を一度に読み込む為 巨大なファイルを扱う場合には 読み込みに懸かる時間や使用するメモリ量に付いて注意が必要で有る -6-