ここでは、ファイルをバイト型配列に読み込む方法、および、バイト型配列をファイルに書き込む方法を紹介します。
注意:テキストファイルを読み込んでその内容を文字列として取得する方法は「文字コードを指定してテキストファイルを読み込む」で紹介しています。
ファイルの内容をバイト型配列に読み込む最も基本的な方法は、FileStreamクラスを使う方法です。
早速ですが、FileStreamクラスのReadメソッドを使って、ファイルの内容を一度にすべて読み込む例を示します。
'ファイルを開く Dim fs As New System.IO.FileStream("C:\test.txt", _ System.IO.FileMode.Open, _ System.IO.FileAccess.Read) 'ファイルを読み込むバイト型配列を作成する Dim bs(fs.Length - 1) As Byte 'ファイルの内容をすべて読み込む fs.Read(bs, 0, bs.Length) '閉じる fs.Close()
//ファイルを開く System.IO.FileStream fs = new System.IO.FileStream( @"C:\test.txt", System.IO.FileMode.Open, System.IO.FileAccess.Read); //ファイルを読み込むバイト型配列を作成する byte[] bs = new byte[fs.Length]; //ファイルの内容をすべて読み込む fs.Read(bs, 0, bs.Length); //閉じる fs.Close();
FileStreamは使用後にCloseメソッドを使って閉じる必要があります。通常は、usingステートメントやtry...finallyを使って確実にCloseメソッドが呼び出されるようにします。詳しくは、「Dispose、Closeが確実に呼び出されるようにする」をご覧ください。
上記の例におけるFileStreamコンストラクタの2番目と3番目のパラメータについては、ここでは説明しません。ファイルを読み込むだけの場合は、このままで問題ありません。これらのパラメータについて詳しくは後述します。
補足:FileStreamはFileShareを指定しないとFileShare.Readとしてファイルを開きます。この場合FileStreamで開いているファイルは、別のプロセス(あるいは自分のプロセス)からの読み込みは可能ですが、書き込みはできません。この設定を変更する方法は、「ファイルをロック(アクセスを制限)する」で紹介します。
補足:File.OpenReadメソッドを使っても、読み取り用のFileStreamオブジェクトを作成することもできます。
大きなファイルを読み込むときは、一度に読み込んでしまうとメモリを消費してしまいます。ファイルのデータを一度にすべて読み込むのではなく、Readを使って少しずつ読み込むこともできます。
以下にその例を示します。
'ファイルを開く Dim fs As New System.IO.FileStream("C:\test.txt", _ System.IO.FileMode.Open, _ System.IO.FileAccess.Read) 'ファイルを一時的に読み込むバイト型配列を作成する Dim bs As Byte() = New Byte(4095) {} 'ファイルをすべて読み込む While True 'ファイルの一部を読み込む Dim readSize As Integer = fs.Read(bs, 0, bs.Length) 'ファイルをすべて読み込んだときは終了する If readSize = 0 Then Exit While End If '部分的に読み込んだデータを使用したコードをここに記述する End While '閉じる fs.Close()
//ファイルを開く System.IO.FileStream fs = new System.IO.FileStream( @"C:\test.txt", System.IO.FileMode.Open, System.IO.FileAccess.Read); //ファイルを一時的に読み込むバイト型配列を作成する byte[] bs = new byte[0x1000]; //ファイルをすべて読み込む for (; ; ) { //ファイルの一部を読み込む int readSize = fs.Read(bs, 0, bs.Length); //ファイルをすべて読み込んだときは終了する if (readSize == 0) break; //部分的に読み込んだデータを使用したコードをここに記述する } //閉じる fs.Close();
注意:文字列をテキストファイルに書き込む方法は「文字コードを指定してテキストファイルに書き込む」で説明しています。
バイト型配列をファイルに書き込むのにも、FileStreamクラスを使います。
次の例では、FileStreamクラスのWriteメソッドを使って、バイト型配列をファイルに書き込んでいます。
'ファイルに書き込むバイト型配列 Dim bs As Byte() = New Byte() {&H44, &H4F, &H42, &H4F, &H4E, &H2E, &H4E, &H45, &H54} 'ファイルを作成して書き込む 'ファイルが存在しているときは、上書きする Dim fs As New System.IO.FileStream("C:\test.txt", _ System.IO.FileMode.Create, _ System.IO.FileAccess.Write) 'バイト型配列の内容をすべて書き込む fs.Write(bs, 0, bs.Length) '閉じる fs.Close()
//ファイルに書き込むバイト型配列 byte[] bs = new byte[] { 0x44, 0x4f, 0x42, 0x4f, 0x4e, 0x2e, 0x4e, 0x45, 0x54 }; //ファイルを作成して書き込む //ファイルが存在しているときは、上書きする System.IO.FileStream fs = new System.IO.FileStream( @"C:\test.txt", System.IO.FileMode.Create, System.IO.FileAccess.Write); //バイト型配列の内容をすべて書き込む fs.Write(bs, 0, bs.Length); //閉じる fs.Close();
上記の例では、FileStreamコンストラクタの2番目のパラメータに「FileMode.Create」を指定していますが、これはファイルが存在しない時は新たに作成し、存在するときは上書きすることを意味します。これを「FileMode.Append」に変更すると、ファイルが存在するときはファイルの末尾に追加して書き込みを行うようになります。
以下にFileMode構造体のメンバを表で説明します。
FileModeのメンバ | 説明 |
---|---|
Append | ファイルが存在する場合は、末尾に追加する(ファイルの末尾をシーク)。存在しない場合は、新たに作成する。書き込み時にのみ使用できる。 |
Create | ファイルが存在する場合は、上書きする。存在しない場合は、新たに作成する。書き込み時にのみ使用できる。 |
CreateNew | ファイルが存在する場合は、System.IO.IOExceptionがスローされる。存在しない場合は、新たに作成する。書き込み時にのみ使用できる。 |
Open | ファイルが存在する場合は、ファイルを開く。存在しない場合は、System.IO.FileNotFoundExceptionがスローされる。 |
OpenOrCreate | ファイルが存在する場合は、ファイルを開く。存在しない場合は、新たに作成する。 |
Truncate | ファイルが存在する場合は、ファイルを開いて、サイズを0に切り捨てる(上書きする)。存在しない場合は、System.IO.FileNotFoundExceptionがスローされる。書き込み時にのみ使用できる。 |
さらに上記の例では、FileStreamコンストラクタの3番目のパラメータに「FileAccess.Write」を指定していますが、これはファイルを書き込みアクセスで開くことを意味します。これを「FileAccess.ReadWrite」にすると、書き込みと読み込みができるようになります。
FileAccessは省略できます。FileAccessを省略した場合は、FileModeがAppendならばWrite、それ以外はReadWriteになります。
補足:File.OpenWriteメソッドを使って、書き込みができるFileStreamオブジェクトを作成することもできます。また、File.Createメソッドを使って、読み取りと書き込みかできるFileStreamオブジェクトを作成することもできます。
上で紹介してきた方法は同期でファイルの読み込み、書き込みを行います。そのため、処理に時間のかかる大きいサイズのファイルを扱う場合は、その間アプリケーションがフリーズしたようになります。これを防ぐ一つの方法が、非同期メソッドを使って処理を行うことです。ファイルの読み込みにはBeginReadとEndReadメソッドを、書き込みにはBeginWriteとEndWriteメソッドを使用します。
非同期メソッドを使うことにより、大きなファイルの処理は通常速くなります(場合によっては逆にパフォーマンスが著しく低下する恐れもあります)。ただし、このような非同期の方法は危険性も高く、マルチスレッドプログラミングの知識がない方にはお勧めできません。マルチスレッドプログラミングと非同期メソッドについては、こちらで説明しています。
非同期でファイルを読み込む例を以下に示します。ここでは、MemoryStreamを使って読み込んだデータをすべてメモリ内に保存しています。なお、フォームにButton1というボタンが配置されていることが前提です。
'読み取るときに使用するバッファ Private readBytes As Byte() = Nothing '読み込んだすべてのデータを保存しておくMemoryStream Private allBytes As System.IO.MemoryStream = Nothing 'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles Button1.Click '初期化 readBytes = New Byte(4095) {} If allBytes IsNot Nothing Then allBytes.Close() End If allBytes = New System.IO.MemoryStream() '読み込むファイルの名前 Dim fileName As String = "C:\test.txt" 'ファイルを開く '非同期の場合は、6番目のパラメータをTrueにする '&H1000はバッファサイズを示す Dim fs As New System.IO.FileStream(fileName, _ System.IO.FileMode.Open, _ System.IO.FileAccess.Read, _ System.IO.FileShare.Read, _ &H1000, _ True) '非同期の読み込みを開始する fs.BeginRead(readBytes, _ 0, _ readBytes.Length, _ New AsyncCallback(AddressOf ReadBytesCallback), _ fs) End Sub '非同期読み取りが終了したときに呼び出されるメソッド Private Sub ReadBytesCallback(ByVal ar As IAsyncResult) 'FileStreamの取得 Dim fs As System.IO.FileStream = _ DirectCast(ar.AsyncState, System.IO.FileStream) '読み込んだサイズを取得 Dim readSize As Integer = fs.EndRead(ar) If readSize > 0 Then '読み込めたとき 'readBytesにreadSizeだけ読み込めている 'MemoryStreamに書き込む allBytes.Write(readBytes, 0, readSize) '残りの読み込みを行う fs.BeginRead(readBytes, _ 0, _ readBytes.Length, _ New AsyncCallback(AddressOf ReadBytesCallback), _ fs) Else 'すべて読み込んだときは終了 fs.Close() End If End Sub
//読み取るときに使用するバッファ private byte[] readBytes = null; //読み込んだすべてのデータを保存しておくMemoryStream System.IO.MemoryStream allBytes = null; //Button1のClickイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //初期化 readBytes = new byte[0x1000]; if (allBytes != null) { allBytes.Close(); } allBytes = new System.IO.MemoryStream(); //読み込むファイルの名前 string fileName = @"C:\test.txt"; //ファイルを開く //非同期の場合は、6番目のパラメータをtrueにする //0x1000はバッファサイズを示す System.IO.FileStream fs = new System.IO.FileStream( fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read, 0x1000, true); //非同期の読み込みを開始する fs.BeginRead(readBytes, 0, readBytes.Length, new AsyncCallback(ReadBytesCallback), fs); } //非同期読み取りが終了したときに呼び出されるメソッド private void ReadBytesCallback(IAsyncResult ar) { //FileStreamの取得 System.IO.FileStream fs = (System.IO.FileStream)ar.AsyncState; //読み込んだサイズを取得 int readSize = fs.EndRead(ar); if (readSize > 0) { //読み込めたとき //readBytesにreadSizeだけ読み込めている //MemoryStreamに書き込む allBytes.Write(readBytes, 0, readSize); //残りの読み込みを行う fs.BeginRead(readBytes, 0, readBytes.Length, new AsyncCallback(ReadBytesCallback), fs); } else { //すべて読み込んだときは終了 fs.Close(); } }
.NET Framework 2.0からは、File.ReadAllBytesメソッドにより、ファイルの内容をすべてバイナリ配列に読み込むことができます。また、File.WriteAllBytesメソッドにより、バイナリ配列をファイルに書き込むことができます。ファイルが既に存在している場合は、上書きします。
これらのメソッドは、データの読み込み、書き込みをした後、ファイルを閉じます。例外が発生しても確実にファイルを閉じてくれます。
以下のとおり、使い方は非常に簡単です。
'ファイルの内容をバイト配列にすべて読み込む Dim bs As Byte() = System.IO.File.ReadAllBytes("C:\test.txt") 'バイト配列をファイルにすべて書き込む System.IO.File.WriteAllBytes("C:\test.bak", bs)
//ファイルの内容をバイト配列にすべて読み込む byte[] bs = System.IO.File.ReadAllBytes(@"C:\test.txt"); //バイト配列をファイルにすべて書き込む System.IO.File.WriteAllBytes(@"C:\test.bak", bs);
補足:VB.NETでは、My.Computer.FileSystem.ReadAllBytesメソッドとMy.Computer.FileSystem.WriteAllBytesメソッドで同じことができます。