LoginSignup
18
5

トゥー・ハード・バイナリ

Last updated at Posted at 2023-12-26

よくきたな。おれは毎日ものすごい量のコードをコミットしているが、publicにする予定はない。
しかし、おまえがやむを得ない事情でバイナリ・ファイルをあつかうばあいについてかいておいた。

それはたとえばブッ壊れたマシンから息も絶え絶えになったメモリカードを救出し、そこから「YYYYMMddHHmmss.bin」だかdatだかウンタラを読みだす必要があるときなどだ。シリアル通信でバイト配列を受信する場合は異なるが、まあ、どのみち似たような流れをたどることだろう。

おれは逆噴射聡一郎先生ではなく、この記事はパロディにすぎない。

テキストとバイナリ

世の中に真の男とそうでもない有象無象がいるように、ファイルには二種類ある。テキストかバイナリだ。
もしおまえがバイナリとは何ぞと思ったなら、ぐだぐだ悩むよりも適当なファイルをひっつまみ、Windows標準のメモ帳の上にドラッグ・アンド・ドロップをするのが早い。
読めたらテキストで、さらに訳が分からなくなったなら、そのファイルはおおむねバイナリだ。いかに例を示す。

image.png

バイナリとは、コンピュータが扱える「0」と「1」で表現されているデータ形式のことだ。人間が読めるかどうか? 答えはノーだ。16しんすうを見ただけですらすらとASCIIコードに変換する一部のギークどもは除く。
おおむねテキストでないものがバイナリだ、と覚えておけばいい。画像ファイルなんかもバイナリだ。なんでもかんでもドラッグ・アンド・ドロップしてみろ。・・・・・・いちぶ読める部分があることもある。画像のヘッダやメタデータなどだ。

しかしここでおまえは疑念をいだきはしなかったか?
バイナリはコンピュータが扱える「0」と「1」で表現されているデータ形式のことだ。だがテキストだってコンピュータが読めるのであり、つまりテキストもバイナリといえるのではないかと、お前のギークな揚げ足取りの魂が訴えたのではなかったか?

その通りだ。実際のところ、コンピュータも人間も読めるのがテキスト、コンピュータだけが読めるのがバイナリといったところだろう。

いずれにしろじっさい的に「これから読み込むファイルがバイナリファイルである」が意味するのは「CSVよりもパッと見でわかりづらいファイルと取っ組み合うことになる」ということに過ぎない。とはいえ、バイナリはパソコンが扱いやすいほんらいの形式である。どう考えても単なるCSVで済むところをわざわざExcelデータにするようなあほさはないため、その点は安心してよい。

サンプルをぶんどれ

ジョブで「バイナリを読め」と言われたばあい、おまえが早急にするべきことは、テキストがどうとか、バイナリがどうとか、読み込むための関数がどうとか、ぐだぐだかんがえることではない。
なにがなくともサンプル・ファイルを確保しろ。
十中百九(10回に100回は)おまえの渡されたデータに関する仕様書は間違っているか古く役に立たない。
uintとintは区別されず仕様書にいっしょくたにして詰め込まれており、絶対に重複しないはずのIDにはなんらかの故障により永遠に0x00が出力されている。リトルエンディアンだと思っていたデータがうまくは読めず、やむを得ず試しにreverseしたらそれらしい値になり、ビッグエンディアンであることが考古学的にわかったりもする。
ゆえに、おまえはまず、厳しい社会から身を守る必要がある。

裏でタルサドゥームが糸を引いており、サンプルファイルがない場合もある。そうであった場合、おまえは内なる狼 -wolf- を奮い立たせ、手を動かし、自力でファイルを作るなどして脅威に立ち向かわなくてはならないかもしれない。

中身をふ瞰しろ

ファイルを確保したおまえは、何らかの用事でバイナリを読む必要が生じるくらいにはギークであるだろうから、おもむろにプログラムを書きはじめ、メイン関数を用意し、早速バイナリを読むコードを書こうとするかもしれない。
まだ早い。おまえはバイナリに慣れていない。生のデータを確認しろ。どこを何バイト読んだら何が書かれているか、・・・・・・狙いを定める必要がある。

バイナリエディタで見るとスッキリとしてわかりやすくなる。
おれはStirlingを勧める。Stirlingは真の男のエディタだ。構造体を扱えるのもよいところだ。
だが好きなものを使え。バイナリ・エディタを入れるということが重要だ。

Stirlingは初はん1998年、できたのが1999年だ。スマッホもない時代の、プレステやゲーム・ボーイといったヴィンテージだがシンプルがゆえにか軽快に動作する。なお、一ぱんに膾炙した固有名詞であり、関係のないサイトがひっかかるため、検索するときは「Stirling バイナリ」にしておけ。

おれの手元にある、テキトーで、かつ、権利のある画像ファイルを開いてみた。
Stirlingでバイナリを見てみたら、左側に16しんすうがあり、右側には運がよければ文字が、あるいは身元不明のシミのら列がある。

image.png

親切にまとまっている通り、この1ますが1バイトだ。

image.png

文字の部分

文字が入っているばあい、直感的には理解しやすいだろう。「バイナリ」とゆわれるからにはおそらくお前の見ているバイナリはASCII(アスキー / US-ASCII)コードで記述されている。

image.png

ASCIIは基本的な文字コードの一種だ。1バイトで1文字を表している。1バイトといえば8ビットであり、256 通りしか存在しないので、漢字とかひらかなとかカタカナなんて表せるわけはないのだが、ASCIIは記号と数字とアルファベットくらいであるためたった1バイトですんでいる。
ASCIIにおいては、0x41が'A'に割り当たっている。

image.png

文字列の'0'は0x30だ。

image.png

文字ではない

image.png

もしもお前が読み込んだファイルが悪夢のようなシミのら列だったばあい、・・・大半はそうなると思うが・・・バイナリを読む必要がある。
例えば、0x20と書かれていたらどう読めばいい?

image.png

じゃあ20なのだろうと安直に考えるよそ者がMEXICOで命を落としている。これは0x20だ。16しんすうで20だ。

普段つかっている電卓にはプログラマにふさわしいモードがある。
左上のハンバーガー・メニューからプログラマを注文しろ。

image.png

16進数で0x20だから、10進数では「32」になる。DECを見ろ。1バイトのデータはこれでよい。あるいはこれをベースに、計算し、補正したりなどして実際の値というやつになるのだろう。

image.png

バイトオーダーに気をつけろ

バイトオーダーには気をつけろ。
shortだとか、2バイトやそれ以上を使ってひとつづきのデータを表現するばあい・・・どっちを頭にするかかはCPUによってきまっている。すなわち、ビッグエンディアンとリトルエンディアン・・・そのバイナリを吐き出したマシンが、MEXICOかU・S・Aか、どちらの社会の掟で生きているかによる。むろん、それはマシンに搭載された魂がそうさせているのであり、やつらに説明するべくもない。バイナリ・ファイルにはわざわざどううゆうおきてだったかをかく欄はとくにないため、判断する手がかりはない。

image.png

この2バイトを読むとき、0D0Aなのか、0A0DなのかはCPUによって決まっている(なお、0D0Aは明らかにの改行コードであるが)。

もしも相手がお前と同じMEXICOで生きてはいない場合、つまりそのバイナリ・ファイルうんたらを出力したマシンが異なるバイトオーダーを持っていた場合、上位バイト野郎と下位バイト野郎のいれかえをしなくてはならない。まあ、C#ともなれば入れ替えといってもreverseを注文してやるだけだが。

バイナリを扱うとき、真の男はまず中身を見るものだ。

image.png

電卓で変換してみろ。仕様書上「年」が入っているはずの部分は、アスキーじゃないなら2バイトだろう(1バイトでは足りない)。手計算で1900ウン年か2000ウン年になったか? つまり、バイナリエディタで見て、2023……07E7になったか? なってない? なっていないならお前かファイルのどちらかがまちがっている。逆の、E707になっているばあい、・・・バイトオーダーが違う。仕様書のどこにもそんなことは書かれていなかったというのに、哀れな奴だ。だがだからと言って己を憐れんでいるうらなり野郎に成り下がる必要もない。そんなやつにはベイブも微笑まない。
すべてを疑うものだけがMEXICOを生き延びることができる。

ともあれ、この辺までがヘッダで、ここからデータが入っているというアタリをつけたりなどして、腑に落ちたらコードを書いていくわけだ。

たとえば、originalByteArrayから10バイトをスキップして、そこからsampleTargetBytesに2バイトを抜き出すような処理はこうなる。

// 最初の10バイトをスキップして次の2バイトを取得
byte[] sampleTargetBytes = originalByteArray.Skip(10).Take(2).ToArray();

(なお、ちゃんとはみ出さないか気を付ける必要がある。)

ASCII

string asciiString = System.Text.Encoding.ASCII.GetString(byteArray);

short型(reverseなし)

byte[] sampleTargetBytes = originalByteArray.Skip(10).Take(2).ToArray();    
short shortValue = BitConverter.ToInt16(sampleTargetBytes, 0);

short型(reverseあり)

byte[] sampleReversedBytes = reversedBytes.Skip(10).Take(2).ToArray();
short reversedShortValue = BitConverter.ToInt16(sampleReversedBytes, 0);

byte

1バイト読みだして、byteにするときはどうなるかって?
そのまま数字とゆうことになる・・・つまりこうだ。

// 最初の10バイトをスキップして次の1バイトを取得
byte value = originalByteArray.Skip(10).Take(1).FirstOrDefault();

お前はコンパスを手に入れる

MEXICOではバイナリエディタを用いればいつでもファイルの中身をのぞけるという気概は生き延びるために役に立つ。おれもBOM付きUTF-8のBOM野郎が邪魔でギットでエラーを起こしていたばあいに命を救われたことがあるためだ。テキストエディタでは気が付きにくいものだった。

参考

Stirling
diehardtales
ASCII.jp 今更ながらコンピュータにおけるエンディアンの話

18
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
5