【C#】バイナリファイル の扱い

 ■ バイナリ を扱うメソッド

 バイナリファイル の扱い

 * FileStreamクラスを利用する

var buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, buffer.Length);

https://docs.microsoft.com/ja-jp/dotnet/api/system.io.filestream.read?view=netframework-4.8

 Byte[]の扱い

 * 以下の関連記事を参照のこと

https://blogs.yahoo.co.jp/dk521123/38080788.html

 ■ サンプル

 例1

// 日本語未対応。半角英数字のみ
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    private const string FilePath = "BinaryFile.bin";

    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      var textData = this.textBox1.Text;
      var binary = Encoding.ASCII.GetBytes(textData);
      this.WriteBinaryFile(binary, FilePath);

      this.textBox1.Text = "Done to Write";
    }

    private void button2_Click(object sender, EventArgs e)
    {
      var binary = this.ReadBinaryFile(FilePath);
      var textData = Encoding.ASCII.GetString(binary);

      this.textBox1.Text = textData;
    }

    public void WriteBinaryFile(byte[] targets, string filePath)
    {
      using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
      {
        fileStream.Write(targets, 0, targets.Length);
      }
    }

    public byte[] ReadBinaryFile(string filePath)
    {
      using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
      {
        var buffer = new byte[fileStream.Length];
        fileStream.Read(buffer, 0, buffer.Length);
        return buffer;
      }
    }
  }
}

 例2:衛星データ(※1)

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      var textData = this.ExtractBinaryData(@"0000008659_001001_ALOS2014410740-140829\LED-ALOS2014410740-140829-UBSL1.5GUA");
      this.textBox1.Text = textData;
    }

    public string ExtractBinaryData(string filePath)
    {
      var binary = ReadBinaryFile(filePath);

      StringBuilder returnValue = new StringBuilder();
      int offset;
      int start;
      int length;

      // offset = 1606052(1881A4)
      offset =
        // ファイルディスクリプタ
        720 +
        // データセットサマリ
        4096 +
        // 地図投影データ(L1.5/3.1 のみ)
        1620 +
        // プラットフォーム位置データ
        4680 +
        // 姿勢データ
        16384 +
        // ラジオメトリックデータ
        9860 +
        // データ品質サマリ
        1620 +
        // 設備関連データ1(ダミー)
        325000 +
        // 設備関連データ2(確定軌道暦)
        511000 +
        // 設備関連データ3(時刻誤差情報)
        3072 +
        // 設備関連データ4(座標変換情報)
        728000;
      start = 0;
      length = 4;
      returnValue.AppendLine(
        string.Format("レコード順序番号 : {0}", this.ToInt32(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 1;
      returnValue.AppendLine(
        string.Format("第1レコードサブタイプコード : {0}", this.ToByte(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 1;
      returnValue.AppendLine(
        string.Format("レコードタイプコード : {0}", this.ToByte(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 1;
      returnValue.AppendLine(
        string.Format("第2レコードサブタイプコード : {0}", this.ToByte(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 1;
      returnValue.AppendLine(
        string.Format("第3レコードサブタイプコード : {0}", this.ToByte(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 4;
      returnValue.AppendLine(
        string.Format("レコード長 : {0}", this.ToInt32(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 4;
      returnValue.AppendLine(string.Format("設備関連データレコード番号('bbb5' bは半角SP) : {0}", this.ToASCII(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 20;
      returnValue.AppendLine(string.Format("係数1(   -8.7033552055E+06) : {0}", this.ToDouble(binary, offset, start, length)));

      offset = offset + length;
      start = start + length;
      length = 20;
      returnValue.AppendLine(string.Format("係数2(    1.9225834432E+05) : {0}", this.ToDouble(binary, offset, start, length)));

      returnValue.AppendLine("後は省略");

      return returnValue.ToString();
    }

    public byte[] ReadBinaryFile(string filePath)
    {
      using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
      {
        var buffer = new byte[fileStream.Length];
        fileStream.Read(buffer, 0, buffer.Length);
        return buffer;
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      byte[] bytes = { 0x00, 0x00, 0x00, 0x0C };

      // If the system architecture is little-endian (that is, little end first),
      // reverse the byte array.
      if (BitConverter.IsLittleEndian)
      {
        Array.Reverse(bytes);
      }
      int value = BitConverter.ToInt32(bytes, 0);
      this.textBox1.Text = string.Format("int: {0}", value);
    }

    private double ToDouble(byte[] bytes, int offset, int start, int length)
    {
      var value = this.ToASCII(bytes, offset, start, length);
      return Double.Parse(value);
    }

    private int ToInt32(byte[] bytes, int offset, int start, int length)
    {
      byte[] targetInBytes = new byte[length];
      Array.Copy(bytes, offset, targetInBytes, 0, length);

      if (BitConverter.IsLittleEndian)
      {
        Array.Reverse(targetInBytes);
      }
      return BitConverter.ToInt32(targetInBytes, 0);
    }

    private byte ToByte(byte[] bytes, int offset, int start, int length)
    {
      byte[] targetInBytes = new byte[length];
      Array.Copy(bytes, offset, targetInBytes, 0, length);
      if (BitConverter.IsLittleEndian)
      {
        Array.Reverse(targetInBytes);
      }
      return targetInBytes[0];
    }

    private string ToASCII(byte[] bytes, int offset, int start, int length)
    {
      byte[] targetInBytes = new byte[length];
      Array.Copy(bytes, offset, targetInBytes, 0, length);
      return Encoding.ASCII.GetString(targetInBytes);
    }
  }
}

 ※1 補足:衛星データ について

 サンプルデータ

https://www.eorc.jaxa.jp/ALOS-2/doc/sam_jindex.htm

の「2014年8月29日 東京 高分解能3mモード (HH偏波) / L1.5 CEOS (zip圧縮ファイル / 1.79 GB)」を使用する

 * CEOS(Comittee on Earth Obsevation Satellites)については、以下のサイト参照

http://rs.aoyaman.com/seminar/about3.html

 フォーマット

https://www.eorc.jaxa.jp/ALOS-2/doc/jformat.htm

より抜粋。(上の「■ サンプル」で使用しているのは、「SAR リーダ」になる)

表 3.1-1 CEOS レベル1.1/1.5/3.1 ファイル命名規約 (P40) より一部抜粋

ファイル種別 ファイル名称規約 ファイル数 内容
ボリュームディレクト VOL-シーンID-プロダクトID 1 ファイルの先頭に位置し、当該ボリューム及びファイルの管理情報を格納する
SAR リーダ LED-シーンID-プロダクトID 1 イメージファイルの前に位置し、後続するファイルの内の画像データと関連のあるアノーテーションデータ、アンシラリデータ等の情報を格納する
SAR イメージ(広域観測モードかつレベル1.1の場合) IMG-偏波情報-シーン ID-プロダクトID-スキャン情報 n(偏波数×スキャン数)|リーダファイルの次に位置し、画像データを格納する
SARイメージ(上記以外の場合) IMG-偏波情報-シーンID-プロダクトID n(偏波数) リーダファイルの次に位置し、画像データを格納する。
SARトレイラ TRL-シーンID-プロダクトID 1 イメージファイルの次に位置し、画像データに関する最終情報を格納する

表 3.2-1 CEOS レベル1.1/1.5/3.1 フォーマットレコード構成 (P46) より一部抜粋

レコード長[byte] レコード数 レコード名
720 1 ファイルディスクリプタ
4096 1 データセットサマリ
1620 1 地図投影データ(L1.5/3.1 のみ)
4680 1 プラットフォーム位置データ
16384 1 姿勢データ
9860 1 ラジオメトリックデータ
1620 1 データ品質サマリ
325000 1 設備関連データ1(ダミー)
511000 1 設備関連データ2(確定軌道暦)
3072 1 設備関連データ3(時刻誤差情報)
728000 1 設備関連データ4(座標変換情報)
5000 1 設備関連データ5(緯度経度変換係数)

3.3. プロダクトフォーマット (P35) より一部抜粋

表 3.3-1~表 3.3-17 に各レコードのフォーマットを示す。
尚、表中において「b」は半角空白文字を表している。
また、「数字)10」は、数字が10 進数で表されていることを示している。
表 3.3-18 にCEOS レベル1.1/1.5/3.1 フォーマットに記述されている項目に対する定義を示す。
また表3.3-19~表3.3-27 にアンテナビーム番号とパラメータを示す。

表 3.2-2 データタイプ一覧 (P79) より一部抜粋

タイプ(略号) 詳細
Im 整数を表現するASCII 文字列(右詰め)
Em.n 実数タイプデータ表示(指数表現、右詰め)
Bm 2 進数表示(1 番目が最上位のバイト、ビッグエンディアン
m:表示桁数
n:小数点以下の桁数
p:指数における乗数

表 3.3-12 設備関連レコード5(1/4) (P79) より一部抜粋

フィールドNo. バイトNo. タイプ 記述(定義と値)
1 1 - 4 B4 レコード順序番号。レベル1.5/3.1の場合 = 12)10
2 5 - 5 B1 第1レコードサブタイプコード = 18)10
3 6 - 6 B1 レコードタイプコード = 200)10
4 7 - 7 B1 第2レコードサブタイプコード = 18)10
5 8 - 8 B1 第3レコードサブタイプコード = 70)10
6 9 - 12 B4 レコード長 = 5000)10
7 13 - 16 I4 設備関連データレコード番号 = 'bbb5'
8 17 - 416 20E20.10 緯度、経度をライン、ピクセルに変換する20の係数

 参考文献

http://www.atmarkit.co.jp/ait/articles/0711/08/news120.html
https://dobon.net/vb/dotnet/file/filestream.html
https://qiita.com/ajillo/items/b61b2d05bbd4180650be
https://www.ipentec.com/document/csharp-filestream-read-write-binary