LoginSignup
3
3

More than 3 years have passed since last update.

文字コードについて、バイナリを眺めながら入門

Last updated at Posted at 2020-12-25

日本語を utf-8 や shift-jis でエンコードしてバイナリを眺めてみる

# utf-8
$ echo あ | nkf -w | hexdump
0000000 e3 81 82 0a
0000004

# utf-8 BOM あり
$ echo あ | nkf -w8 | hexdump
0000000 ef bb bf e3 81 82 0a
0000007

# shift-jis
$ echo あ | nkf -s | hexdump
0000000 82 a0 0a
0000003

行末の 0a は改行コードです、つまり LF

のバイト数ですが、
utf-8 だと e3 81 82 3バイトです。
shift-jis だと 82 a0 2バイトです。

愛を分解してみる

U+611B

これを utf-8 でエンコードすると 3バイトの

e6 84 9b

となる。

参考. https://elite-lane.com/difference-between-unicode-and-utf-8-and-utf-16-and-utf-32/

文字化けを再現してみる

「ようこそ」という文字列を Shift JIS でエンコードして保存してみます。それを hexdump で見ると以下のようになっています。

welcome_shift_jis.txt
ようこそ

mac のターミナルで以下のような感じに見えます。

$ hexdump welcome_shift_jis.txt
0000000 82 e6 82 a4 82 b1 82 bb 0a
0000009

$ cat welcome_shift_jis.txt
�悤����

文字化けが発生しています。

ここで「悤」に注目してみます。

Shift JIS でエンコードされたバイナリは、行末の改行( 0a ) を除くと

 82 e6 82 a4 82 b1 82 bb

です。そしてこれを、utf-8 で解釈すると、

e6 82 a4

この部分が以下にヒットします。
https://www.compart.com/en/unicode/U+60A4

Unicode Character “悤” (U+60A4)
UTF-8 Encoding: 0xE6 0x82 0xA4

Ruby の File.open のエンコードの挙動

>> File.open('test1', 'w:utf-8'){|f| f.write('ようこそ'.encode('cp932'))} #=> 12
>> cat 'test1' #=> "ようこそ"

>> File.open('test1', 'w'){|f| f.write('ようこそ'.encode('cp932'))} #=> 8
>> cat 'test1' #=> "\x82悤\x82\xB1\x82\xBB"

ext_enc(外部エンコーディング)が指定されている場合、読み込まれた文字列にはこのエンコーディングが指定され、出力する文字列はそのエンコーディングに変換されます。

Mac の Excel で開いて文字化けするか見てみる。

律儀に以下のパターンで csv ファイルを作ってみました。

$ tree .
.
├── utf16_BE_with_bom.csv
├── utf16_BE_without_bom.csv
├── utf16_LE_with_bom.csv
├── utf16_LE_without_bom.csv
├── utf8_with_bom.csv
└── utf8_without_bom.csv

たとえば、以下のような感じでファイルは作っています。

cf. https://kazmax.zpp.jp/cmd/n/nkf.1.html

$ echo ようこそ | nkf -w16L0 > utf16_LE_without_bom.csv

結果はこうなりました。

image.png

  • UTF-8 BOM 有り
  • UTF-16 LE BOM 有り

この 2パターンは文字化けせずに表示されました。

それぞれの hexdump はこちら

bash-3.2$ ls -1 | while read line; do echo $line; hexdump $line; echo -e '\n'; done
utf16_BE_with_bom.csv
0000000 fe ff 30 88 30 46 30 53 30 5d 00 0a
000000c


utf16_BE_without_bom.csv
0000000 30 88 30 46 30 53 30 5d 00 0a
000000a


utf16_LE_with_bom.csv
0000000 ff fe 88 30 46 30 53 30 5d 30 0a 00
000000c


utf16_LE_without_bom.csv
0000000 88 30 46 30 53 30 5d 30 0a 00
000000a


utf8_with_bom.csv
0000000 ef bb bf e3 82 88 e3 81 86 e3 81 93 e3 81 9d 0a
0000010


utf8_without_bom.csv
0000000 e3 82 88 e3 81 86 e3 81 93 e3 81 9d 0a
000000d

fish も書いておこう。

$ ls | while read file; echo $file; hexdump $file; echo \n; end

バイナリを眺める

$ echo > a.txt
~/Desktop
$ cat a.txt
あ
~/Desktop
$ hexdump a.txt
0000000 e3 81 82 0a
0000004
~/Desktop
$ xxd a.txt
00000000: e381 820a                                ....
~/Desktop
$ xxd -b a.txt
00000000: 11100011 10000001 10000010 00001010

参考リンクやおまけ

https://itsakura.com/it-unicode-utf
https://elite-lane.com/difference-between-unicode-and-utf-8-and-utf-16-and-utf-32/

UTF-16は、「0xD842」と「0xDFB7」の2つになっています。2バイト*2で4バイト使用しています。上位サロゲートと下位サロゲートといいます。
UTF-8は、「F0 A0 AE B7」です。4バイト使用しています。
ちなみにデータベース「MySQL」のUTF-8 mb4は、4バイトに対応しているUTF-8のことです。

そーだったんだ。

サロゲートペアについては上の参考リンクで理解できます。

0x というのは16進数表記という意味。

3
3
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
3
3