Diary

Diary

日々学んだことをアウトプットする場として初めてみました

Linux コマンドで文字コードを変換する

今回は、Shift_JISutf-8 の変換などについて扱います。

[目次]

文字コード

以降で扱うために必要な、文字コードの知識・並びに Linux の標準文字コードである utf-8 を簡単に説明します。

あまり詳しくないため、その辺はご容赦ください。

xxd で実験してみる

# 16 進数ダンプ等をしてくれます
$ man xxd
xxd - make a hexdump or do the reverse.
...

英語と日本語を試しに xxd に突っ込んでみます。

2**8 = 16 * 16 なので、16 進数2文字分で 1 バイトを表します。

# 簡単な例
$ echo a | xxd -p
610a
$ echo| xxd -p
e381820a

行末に共通している 0a は、Linux での改行コード(LF)です。

それを除いて表示すると、以下のように「a」は1バイト、「あ」は 3 バイトで表されていることがわかります。

$ printf a | xxd -p
61
$ printf あ | xxd -p
e38182

バイナリで表示するには、以下のように -b オプション(binary)をつけてあげます。

$ printf a | xxd -b
00000000: 01100001      a

元に戻す

標準の echo を使うと簡単にできます。

$ echo -e '\x61'
a
$ echo -e '\xE3\x81\x82'

1バイトずつチェック

# 基数を変換
$ echo -e '61\nE38182' | sed '1iibase=16; obase=2' | bc
1100001
111000111000000110000010
# 1 バイトずつに区切って整形して表示
$ echo -e '61\nE38182' | sed '1iibase=16; obase=2' |\
 bc | xargs printf "%24s\n" | sed -r 's/.{8}/& /g'
                   1100001 
11100011 10000001 10000010 

わかること(調べてわかったこと)

  • UTF-8 は可変長
  • 「a」は1バイト (ASCII 文字)
    • ASCII 文字列は、7 ビット分だけ埋まっている。
    • 残り1ビットは 0 が自動で埋まる??
  • 「あ」は 3バイト
    • 先頭(1バイト目)は「1110」で始まる
    • 続く中身に関しては、「10」で始めることで区別
    • 上記は、ASCII の先頭が「0」であることと差別化できており区別可能

2進数から文字列を復元

# まず、解析に使いたいバイナリデータを作成する
$ echo 'aあ' | xxd -b | awk '{$1=""; print}' | tr -dc '01'
0110000111100011100000011000001000001010

# 16 進数に変換する
## obase が先、ibase が後
### ibase を先に書くと、obase=16 の 16 が2進数として解釈されていまう
$ echo '0110000111100011100000011000001000001010' |\
 sed '1iobase=16; ibase=2' | bc
61E381820A
### 失敗した例
$ echo '0110000111100011100000011000001000001010' |\
 sed '1iibase=2; obase=16' | bc
1111012012101212001022112

# xxd -p -r: 16 真数を羅列したテキストを読んでデコードする
$ echo '0110000111100011100000011000001000001010' |\
 sed '1iobase=16; ibase=2' | bc | xxd -p -r
aあ

nkf

Shift_JIS のファイルを UTF-8 に変換する

基本的には、nkf -wLuxを使います

# 普通に cat してみると、文字化けする
$ cat shift_jis_test.txt 
Shift_JIS �̃e�X�g�ł�
�V�t�g�W�X�e�X�g
����ɂ��͐��E

# nkf コマンドを使う
$ nkf -wLux shift_jis_test.txt 
Shift_JIS のテストです
シフトジステスト
こんにちは世界

オプションについて簡単に説明しておきます。

  • -Lu は、CRLF をラインフィード(LF)だけに変換する
  • -w は、テキストを UTF-8 に変える
  • -x は、カタカナが半角になるのを防止する

UTF-8 のファイルを Shift_JIS に変換する

nkf の -s オプションなどを使います。

$ echo 吾輩は猫である。 | nkf -sLwx
��y�͔L�ł���B
$ echo 吾輩は猫である。 | nkf -sLwx | nkf -wLux
吾輩は猫である。
  • -s は、Shift_JIS への変換
  • -Lw は、LF から CRLF への変換

おまけ

まとめ

  • Shift_JIS to UTF-8 変換にはnkf -wLuxを使う
  • xxd -pで 16 進数変換する(-b で 2 進数)
  • xxd -p -rで 16 進数から文字に戻す
  • 数字の基数の変換では、bc を使う
    • echo 'obase=2; 3' | bc

おまけ:アルファベットは大文字で

基本的に 16 進数中のアルファベットは大文字で表すのが吉

# a, あ の UTF-8 の文字コード(16 進数)を2進数に戻したときの例
## なぜか「あ」だけ変換されなかった
$ echo -e '61\ne38182' | sed '1iibase=16; obase=2' | bc
1100001
0
## e を E に変更したらうまくいった
$ echo -e '61\nE38182' | sed '1iibase=16; obase=2' | bc
1100001
111000111000000110000010

bc の仕様か?

おわりに

今回しらべる中で、文字コードについて少し理解が深まった気がします。