11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

バイナリを見て文字コードを判別する方法

Last updated at Posted at 2019-04-03

最近文字コードの勉強をしたところ、人力でもある程度は文字コードを判別できそうな気がしてきたのでやってみました。
サーバ上のコンフィグやらスクリプトやらのコメントが文字化けしたときに、さくっと文字コードを当てられたりしたらかっこいいですよね。

今回は日本語の文章が次のうちのどの文字コードか判別したいと思います。

  • UTF-8
  • EUC-JP
  • Shift_JIS
  • ISO-2022-JP

なお、この記事で言う「日本語」とは以下の文字を指すことにします。

  • 全角ひらがな
  • 全角カタカナ
  • 漢字(JIS第1水準くらいまでをイメージ)

    ただ実際の文章では日本語だけでなく半角英数字が混ざっていることも多いかと思うので、例文は以下にしました。
2019年5月より元号は令和<REIWA>

この文章をiconvでいろいろな文字コードに変換し、hexdumpでバイナリを読み解いていきます。

※この記事は正確には正しくない表現があるかもしれません。hexdumpを軽く見てなんとなく文字コードがわかればいいな程度に書いています。

Step0 hexdumpについて

hexdumpはバイナリデータを16進数で表示させることができるツールです。
-Cオプションをつけないと2バイトごとに順序が入れ替わって表示されるようなので注意してください。

Step1 ISO-2022-JPか?

まずは4つの文字コードの中で、最もわかりやすい気がしているISO-2022-JPから見ていきます。

この文字コードはSMTPなどで日本語をやり取りできるように1バイトのうち7ビットまでしか使われていません。
つまり、最上位の1ビットは常に0。16進数に変換すると0x80から0xFFまでは一切使用しません。
hexdumpの結果を見て、日本語の文章であるはずなのに0x7F以下の値しか登場しなければISO-2022-JPであると判断できます。

$ echo "元号は2019年5月より令和<REIWA>" | iconv -t ISO-2022-JP | hexdump -C
00000000  1b 24 42 38 35 39 66 24  4f 1b 28 42 32 30 31 39  |.$B859f$O.(B2019|
00000010  1b 24 42 47 2f 1b 28 42  35 1b 24 42 37 6e 24 68  |.$BG/.(B5.$B7n$h|
00000020  24 6a 4e 61 4f 42 1b 28  42 3c 52 45 49 57 41 3e  |$jNaOB.(B<REIWA>|
00000030  0a                                                |.|
00000031

Step2 UTF-8か?

これも比較的わかりやすい気がしています。
ただあまりメジャーではない漢字や絵文字などが含まれていると、必ずしもここに書いたとおりではないかもしれません。

UTF-8の日本語はだいたい先頭の4ビットが0xEから始まる3バイトであることが多いです。
つまり、頻繁に0xE? ?? ??が何度も繰り返し登場してきたときはUTF-8の可能性が高いと言えるでしょう。

また、UTF-8はASCIIコードと互換性があるので半角英数字は必ず最上位ビットが0の1バイトで表現されます。
したがって日本語の文章の中に半角英数字が混ざっていれば0x7F以下のバイトも登場します。

$ echo "元号は2019年5月より令和<REIWA>" | iconv -t UTF-8 | hexdump -C
00000000  e5 85 83 e5 8f b7 e3 81  af 32 30 31 39 e5 b9 b4  |.........2019...|
00000010  35 e6 9c 88 e3 82 88 e3  82 8a e4 bb a4 e5 92 8c  |5...............|
00000020  3c 52 45 49 57 41 3e 0a                           |<REIWA>.|
00000028

Step3 EUC-JPか?

EUC-JPでは、日本語は最上位ビットが常に1である2バイトのコートで表されます。ただし諸事象あって0x80から0xA0までは使用されません。
したがって先頭の文字が0xA以上のバイトが偶数個並んでいればEUC-JPの可能性があります。
※ただしまれに0x8Eか0x8Fが登場する場合があります。

またEUC-JPもASCIIコードと互換性があるので、半角英数字が文章に混ざっていれば、そこだけは0x7F以下になります。

$ echo "元号は2019年5月より令和<REIWA>" | iconv -t EUC-JP | hexdump -C
00000000  b8 b5 b9 e6 a4 cf 32 30  31 39 c7 af 35 b7 ee a4  |......2019..5...|
00000010  e8 a4 ea ce e1 cf c2 3c  52 45 49 57 41 3e 0a     |.......<REIWA>.|
0000001f

Step4 Shift_JISか?

Shift_JISは一番掴みどころがない印象です。というわけでやっつけ感がありますが、一番最後まで残ればShift_JIS。

一応EUC-JPと異なる点を書いておくと、2バイトで日本語一文字ではあるんですが、2バイト目の最上位ビットが0になる可能性があります。
つまり先頭の文字が0x80以上のバイトが奇数個並んでいればShift_JISの可能性があります。
※まれに8Eか8Fが登場したせいでEUC-JPも奇数個になってしまう可能性はある。

また、半角カタカナを使用しなければ0xA0から0xDFが登場することがない点もEUC-JPと異なります。

$ echo "元号は2019年5月より令和<REIWA>" | iconv -t SJIS | hexdump -C
00000000  8c b3 8d 86 82 cd 32 30  31 39 94 4e 35 8c 8e 82  |......2019.N5...|
00000010  e6 82 e8 97 df 98 61 3c  52 45 49 57 41 3e 0a     |......a<REIWA>.|
0000001f

まとめ

Shift_JISとEUC-JPの切り分けがイマイチな感じになってしまいました。もっとわかりやすくまとめられたら、、、

本題とは関係ないですが、hexdumpの横っちょにASCIIで表示された様子をみて、なるほどISO-2022-JPとShift_JISはASCIIとの互換性がイマイチだなと実感しました。

ちなみにこの記事の内容は以下の書籍で勉強させていただきました。
文字コードについてもっと詳しくまとめられていておすすめです。
https://gihyo.jp/book/2019/978-4-297-10291-3

11
10
0

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
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?