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

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

`レコード長[byte]`レコード数レコード名
7201ファイルディスクリプタ
40961データセットサマリ
16201地図投影データ(L1.5/3.1 のみ)
46801プラットフォーム位置データ
163841姿勢データ
98601ラジオメトリックデータ
16201データ品質サマリ
3250001設備関連データ1(ダミー)
5110001設備関連データ2(確定軌道暦)
30721設備関連データ3(時刻誤差情報)
7280001設備関連データ4(座標変換情報)
50001設備関連データ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実数タイプデータ表示(指数表現、右詰め)
Bm2 進数表示(1 番目が最上位のバイト、ビッグエンディアン

m:表示桁数
n:小数点以下の桁数
p:指数における乗数

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

`フィールドNo.`バイトNo.`タイプ`記述(定義と値)
11 - 4B4レコード順序番号。レベル1.5/3.1の場合 = 12)10
25 - 5B1第1レコードサブタイプコード = 18)10
36 - 6B1レコードタイプコード = 200)10
47 - 7B1第2レコードサブタイプコード = 18)10
58 - 8B1第3レコードサブタイプコード = 70)10
69 - 12B4レコード長 = 5000)10
713 - 16I4設備関連データレコード番号 = 'bbb5'
817 - 41620E20.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

関連記事

C#】Byte / Byte[] (Byteの配列)

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

ビッグエンディアン / リトルエンディアン

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

【トラブル】【C#】ファイルアクセス時のトラブルシュート

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

使用したソフト ~ バイナリエディタ 編 ~

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