バイナリファイルの読み書きをする - FileStreamを利用 - C#

C#でバイナリファイルの読み書きをしたい場合があります。この記事では、FileStreamを用いてファイルにバイナリデーターの読み書きをするコードを紹介します。

概要

FileStreamを用いてアクセスしたファイルにバイナリデータを書き込む場合は、FileStreamからBinaryReader, BinaryWriterのオブジェクトを作成することでバイナリデータの読み書きができます。

UI

以下のUIを作成します。ボタンを2つ、テキストボックスを1つ、openFileDialog, saveFileDialogを配置します。

コード

以下のコードを記述します。
data.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FileStreamBinary
{
  public class data
  {
    public long id;
    public short code;
    public byte[] name;
    public decimal price;
  }
}
FormMain.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace FileStreamBinary
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
        data d = new data();
        d.id = 5;
        d.code=102;
        d.name = new byte[32];
        //d.name = System.Text.Encoding.ASCII.GetBytes("Penguin");
        System.Text.Encoding.ASCII.GetBytes("Penguin",0,7,d.name,0);
        d.price = 190;
        
        FileStream fs = new FileStream(saveFileDialog1.FileName, FileMode.Create);
        BinaryWriter bw = new BinaryWriter(fs);
        
        bw.Write(d.id);
        bw.Write(d.code);
        bw.Write(d.name);
        bw.Write(d.price);

        bw.Close();
        fs.Close();

        textBox1.Text = "保存しました。\r\n";
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
        data d = new data();

        FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open);
        BinaryReader br = new BinaryReader(fs);
        d.id = br.ReadInt64();
        d.code = br.ReadInt16();
        d.name = br.ReadBytes(32);
        string text = System.Text.Encoding.ASCII.GetString(d.name);
        text = text.TrimEnd('\0');
        d.price = br.ReadDecimal();

        textBox1.Text += "読み込みが完了しました。\r\n";
        textBox1.Text += string.Format("{0:d}\r\n", d.id);
        textBox1.Text += string.Format("{0:d}\r\n", d.code);
        textBox1.Text += string.Format("{0:s}\r\n", text);
        textBox1.Text += string.Format("{0:g}\r\n", d.price);
      }
    }
  }
}

解説

バイナリファイルの書き込み

下記コードがバイナリファイルを書き込むコードです。saveFileDialogでファイルが選択された場合に処理を実行します。データを格納するdataクラスのインスタンスを作成し、メンバ変数にデータを設定します。nameフィールドはbyteの長さ32固定フィールドのため、まずnewで32バイト分の配列を確保し、Encoding.GetBytes(Char[], Int32, Int32, Byte[], Int32)のタイプのメソッドを呼び出しバッファに文字rつを設定します。ここでGetBytes(Char[])のメソッドを呼び出した場合、配列が初期化されてしまうため、長さが文字列の長さちょうどになるため固定長でなくなり正しく動作しません。
ファイルへの書き込みは、FileStreamのインスタンスを作成後、BinaryWriterのインスタンスを作成し、BinaryWriterのWriteメソッドを呼び出してデータを書き込みます。データの書き込み後ストリームを閉じます。
  if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
    data d = new data();
    d.id = 5;
    d.code=102;
    d.name = new byte[32];
    System.Text.Encoding.ASCII.GetBytes("Penguin",0,7,d.name,0);
    d.price = 190;
        
    FileStream fs = new FileStream(saveFileDialog1.FileName, FileMode.Create);
    BinaryWriter bw = new BinaryWriter(fs);
        
    bw.Write(d.id);
    bw.Write(d.code);
    bw.Write(d.name);
    bw.Write(d.price);

    bw.Close();
    fs.Close();

    textBox1.Text = "保存しました。\r\n";
  }

バイナリファイルの読み込み

バイナリファイルを読み込むコードは下記です。ファイルを開くダイアログでファイルが選択された際に処理を実行します。はじめにファイルから読み取った情報を格納するdataクラスのインスタンスを作成します。ファイルの読み取りは、FileStreamでファイルを開いたのちファイルストリームを与えてBinaryReaderのインスタンスを作成します。データの読み込みはBinaryReaderのメソッドを呼び出します。longの値を読み込む場合はReadInt64, shortの値を読み込む場合はReadInt16, decimalの値を読み込む場合はReadDecimalメソッドを呼び出します。固定長の文字列を読み出す場合はReadBytes()メソッドでバイト配列を読み出し、その後EncodingクラスのGetString()メソッドを呼び出し、byte配列から文字列を取得します。(詳しくはこちら)
byte配列では末尾以降の文字列が'\0'になっているため、TrimEnd('\0');を呼び出し終端文字列を削除しています。(詳しくはこちら)
ファイルの読み込みが終了した後、Close()メソッドを呼び出しストリームを閉じます。また、読み込んだデータをTextBoxに表示します。
  if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
    data d = new data();

    FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open);
    BinaryReader br = new BinaryReader(fs);
    d.id = br.ReadInt64();
    d.code = br.ReadInt16();
    d.name = br.ReadBytes(32);
    string text = System.Text.Encoding.ASCII.GetString(d.name);
    text = text.TrimEnd('\0');
    d.price = br.ReadDecimal();
    
    br.Close();
    fs.Close();

    textBox1.Text += "読み込みが完了しました。\r\n";
    textBox1.Text += string.Format("{0:d}\r\n", d.id);
    textBox1.Text += string.Format("{0:d}\r\n", d.code);
    textBox1.Text += string.Format("{0:s}\r\n", text);
    textBox1.Text += string.Format("{0:g}\r\n", d.price);

  }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。

ファイルの書き込み

[button1]をクリックします。名前を付けて保存ダイアログが表示されます。保存するファイル名を入力します。


ファイルが保存されテキストボックスにメッセージが表示されます。


ファイルが作成されました。


ファイルをバイナリエディタで開くとデータが書き込まれていることがわかります。


ファイルの読み込み

アプリケーションを起動し[button2]ボタンをクリックします。下図の[開く]ダイアログが表示されますので、先ほど保存したバイナリファイルを開きます。


バイナリファイルからデータを読み取り、読み取ったデータがテキストボックスに表示されます。先に書き込んだデータと同じものが表示されています。


以上でバイナリファイルの読み書きができました。

補足

C++ Delphiで構造体をそのまま書き込む"型つきファイル"はC#での実現は難しいです。他のプログラムで書き出したバイナリファイルを読み取る場合は1フィールドごとにBinaryReader/BinaryWriterで読み書きしたほうが確実です。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2013-08-08
iPentec all rights reserverd.