od・xxd入門|ファイルの中身をバイト単位で確かめる

od・xxd入門|ファイルの中身をバイト単位で確かめる

テキストエディタで開いても原因が分からないファイルに、ときどき出会います。

見た目は普通なのに、プログラムに渡すと落ちる。

そういうときは、中身をバイト単位で覗いてしまうのが早道です。

今回は od と xxd を使って、ファイルの中身を16進数で確認する手順をまとめました。

検証は GNU coreutils 8.32 の od と、xxd 2021-10-22 版で行っています。

od と xxd はどう違うのか

どちらもファイルを16進数などで表示するコマンドですが、出自は別もの。

od は GNU coreutils に含まれていて、たいていの環境に最初から入っています。

一方の xxd は vim に付属していて、ディストリによっては vim 関連パッケージを入れて初めて使えるツールです。

使い分けは単純で、読みやすさなら xxd、表示形式の細かい切り替えなら od

さらに xxd には16進ダンプから元のバイト列へ戻す機能があり、od にはこれがありません。

$ od --version | head -1
od (GNU coreutils) 8.32

$ xxd -v
xxd 2021-10-22 by Juergen Weigert et al.

まずは od でバイト列を見る

サンプルとして、日本語を含む小さな CSV を作ります。

printf なら改行の有無まで自分で決められるので、この手の検証にはうってつけ。

$ printf 'ID,名前\n1,かもめ\n' > utf8.csv
$ od -c utf8.csv
0000000   I   D   , 345 220 215 345 211 215  \n   1   , 343 201 213 343
0000020 202 202 343 202 201  \n
0000026

左端の 0000000 はファイル先頭からの位置で、既定では8進数で表示されます。

ASCII に収まる文字はそのまま出て、それ以外は数値に置き換わる仕組み。

「名」が 345 220 215 の3バイトになっているのは、od -c が数値を8進数で書くためです。

16進で読みたいときは -tx1 を足します。

$ od -An -tx1 -c bom.csv | head -3
  ef  bb  bf  49  44  2c  4e  61  6d  65  0a  31  2c  67  75  6c
 357 273 277   I   D   ,   N   a   m   e  \n   1   ,   g   u   l
  6c  0a

-An はオフセット列を消すオプションで、出力を貼り付けて比較したいときに効きます。

文字化けそのものの仕組みは、別の記事で整理しました。

文字化けはなぜ起きる?文字コード(UTF-8・Shift_JIS)とBOMの基礎

xxd なら16進とASCIIを同時に読める

同じ要領で、CRLF 改行の CSV を xxd に通してみます。

$ printf 'ID,Name\r\n1,gull\r\n' > crlf.csv
$ xxd crlf.csv
00000000: 4944 2c4e 616d 650d 0a31 2c67 756c 6c0d  ID,Name..1,gull.
00000010: 0a                                       .

左に16進、右に ASCII が並ぶのが xxd の標準の出力です。

ただし右側では表示できない文字がすべてピリオドに潰れるので、改行とタブの区別は付きません。

つまり 判断はいつも左の16進で行うのが鉄則。

この例では 0d 0a が2か所に並んでいるため、CRLF 改行だと確定できます。

改行コードの背景については、こちらにまとめました。

改行コードCR・LF・CRLFの違いと仕組み

BOM と末尾の改行を見つける

Excel が書き出した CSV でよく引っかかるのが BOM です。

$ xxd bom.csv | head -2
00000000: efbb bf49 442c 4e61 6d65 0a31 2c67 756c  ...ID,Name.1,gul
00000010: 6c0a                                     l.

先頭の ef bb bf が UTF-8 の BOM で、1列目のヘッダ名が ID と一致しないという定番の事故は、たいていこの3バイトが原因。

当たりを付けるだけなら file コマンドでも足ります。

$ file utf8.csv crlf.csv bom.csv
utf8.csv: Unicode text, UTF-8 text
crlf.csv: ASCII text, with CRLF line terminators
bom.csv:  Unicode text, UTF-8 (with BOM) text

file で当たりを付けて、od か xxd で裏を取る。

この順番だと調査がいちばん速く終わります。

末尾に改行があるかどうかの確認も、終わりの数バイトを切り出すだけ。

$ printf 'no newline at end' > tail1.txt
$ tail -c 20 tail1.txt | xxd | tail -1
00000010: 64                                       d

$ printf 'has newline at end\n' > tail2.txt
$ tail -c 20 tail2.txt | xxd | tail -1
00000010: 6e64 0a                                  nd.

0a で終わっていなければ、そのファイルには末尾の改行がありません。

全角スペースのような「見えない差」も見える

目視ではまず見つからないのが、半角スペースと全角スペースの混在です。

$ printf 'a\xe3\x80\x80b' > zenkaku.txt
$ xxd zenkaku.txt
00000000: 61e3 8080 620a                           a...b.

e3 80 80 が全角スペース(U+3000)の UTF-8 表現。

設定ファイルのインデントに紛れ込むと、パーサが理由も言わずに落ちます

エディタで空白の可視化を切っていると、何時間でも溶かせる種類のバグ。

範囲を絞る、そして元に戻す

ファイルが大きいときは、見たい場所だけ切り出します。

$ xxd -s 3 -l 7 bom.csv
00000003: 4944 2c4e 616d 65                        ID,Name

-s が開始位置、-l が長さです。

BOM の3バイトを飛ばしてやると、ちゃんと ID,Name が読めました。

xxd のもう一つの武器が、16進ダンプから元のバイト列へ戻す -r。

$ xxd -p crlf.csv
49442c4e616d650d0a312c67756c6c0d0a

$ xxd -p crlf.csv | xxd -r -p | od -c | head -2
0000000   I   D   ,   N   a   m   e  \r  \n   1   ,   g   u   l   l  \r
0000020  \n

-p はオフセットも ASCII 欄も無いプレーンな16進出力です。

これを -r -p に流すと、元のバイト列がそのまま復元されます。

壊れた1バイトだけを書き換えて戻す、といった荒業もこの組み合わせで通るのです。

最後に

od と xxd は、ファイルを「文字の列」ではなく「バイトの列」として見せてくれる道具です。

文字化け、改行コード、BOM、全角スペース。

どれもエディタの画面では見分けが付かないのに、16進で開けば一目で違います。

原因の分からないファイルに当たったら、まず xxd に通してみてください。

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA