文字エンコードとロケールを体感する

前回からの続き。

今時、OSX環境ならUTF-8で使っていれば何の問題もないだろう、と信じていると痛い目に遭う。

文字化けする現象

  • AppleScriptエディタで、ファイルフォーマット=テキストで保存してみる。
tell application "Finder"
	display dialog "こんばんは"
end tell


____ここから、ターミナルの環境設定 >> 設定 >> 詳細 >> 言語環境:文字エンコーディング=日本語(UTF-8)____

  • 現在の環境は、ターミナルの環境設定 >> 設定 >> 詳細で、文字エンコーディング=日本語(UTF-8)になっている。 

  • 新規タブを開いて、catコマンドで確認すると...
$ cat $HOME/Library/Scripts/encoding_test.applescript
tell application "Finder"
	display dialog "????΂??"
end tell
  • 見事に文字化けしている。

正しく表示される場合

  • 再び、テキストエディットを開いて標準テキストの新規書類に、まったく同じ内容を手入力してみる。
tell application "Finder"
	display dialog "こんばんは"
end tell
  • catコマンドで確認すると...
$ cat $HOME/Documents/encoding_test.txt
tell application "Finder"
	display dialog "こんばんは"
end
  • 文字化けせずに正常に表示された。

ダンプ表示で違いを知る

  • この違いを知るには、odコマンドでダンプ(16進数コード)表示してみると、よく分かる。


____ここから、ターミナルの環境設定 >> 設定 >> 詳細 >> 言語環境:文字エンコーディング=日本語(Shift JIS)____

  • その前に、ターミナルの環境設定 >> 設定 >> 詳細で、文字エンコーディング=日本語(Shift JIS)にしておく。

  • ターミナルで新規タブを開いて、以下のコマンドを実行した。
$ od -tx1c $HOME/Library/Scripts/encoding_test.applescript
0000000    74  65  6c  6c  20  61  70  70  6c  69  63  61  74  69  6f  6e
           t   e   l   l       a   p   p   l   i   c   a   t   i   o   n
0000020    20  22  46  69  6e  64  65  72  22  0a  09  64  69  73  70  6c
               "   F   i   n   d   e   r   "  \n  \t   d   i   s   p   l
0000040    61  79  20  64  69  61  6c  6f  67  20  22  82  b1  82  f1  82
           a   y       d   i   a   l   o   g       "   こ  **   ん  **   ば
0000060    ce  82  f1  82  cd  22  0a  65  6e  64  20  74  65  6c  6c    
          **   ん  **   は  **   "  \n   e   n   d       t   e   l   l    
0000077
  • ターミナルの設定により、この新規タブの言語環境はShift JISになっている。
  • そして、Shift JIS環境なら、文字化けしないで表示される。
$ cat  $HOME/Library/Scripts/encoding_test.applescript
tell application "Finder"
	display dialog "こんばんは"
end tell
  • つまり、encoding_test.applescriptは、Shift JISでエンコードされたテキストなのだ。
  • Shift JISの言語環境で、Shift JISでエンコードされたテキストならば、正常に表示される。


____ここから、ターミナルの環境設定 >> 設定 >> 詳細 >> 言語環境:文字エンコーディング=日本語(UTF-8)____

  • ふたたび、ターミナルの環境設定 >> 設定 >> 詳細で、文字エンコーディング=日本語(UTF-8)に戻しておく。

  • ターミナルで最初のタブに戻って、以下のコマンドを実行した。
$ od -tx1c  $HOME/Documents/encoding_test.txt
0000000    74  65  6c  6c  20  61  70  70  6c  69  63  61  74  69  6f  6e
           t   e   l   l       a   p   p   l   i   c   a   t   i   o   n
0000020    20  22  46  69  6e  64  65  72  22  0a  09  64  69  73  70  6c
               "   F   i   n   d   e   r   "  \n  \t   d   i   s   p   l
0000040    61  79  20  64  69  61  6c  6f  67  20  22  e3  81  93  e3  82
           a   y       d   i   a   l   o   g       "  こ  **  **  ん  **
0000060    93  e3  81  b0  e3  82  93  e3  81  af  22  0a  65  6e  64    
          **  ば  **  **  ん  **  **  は  **  **   "  \n   e   n   d    
0000077
  • オレンジ色の太字がその違い。
  • 「こんばんは」の部分である。
  • 16進数ダンプ表示された文字コードの値も、バイト数も、まったく違っているのだ!
  • ターミナルの設定により、こちらのタブの言語環境はUTF-8である。
  • そして、encoding_test.txtもUTF-8である。(テキストエディットで保存する時にUTF-8を指定したので)
$ cat $HOME/Documents/encoding_test.txt
tell application "Finder"
	display dialog "こんばんは"
end


言語環境と、テキストのエンコード方式は、一致させる必要があるのだ!

  • 一致すれば正常に表示され、違っていると文字化けする。

ターミナルの言語環境とコマンドのロケール

  • ややこしいのは、二つの言語環境があること。
    • ターミナルの設定としての言語環境と、
    • コマンド実行環境としてのロケール
  • 通常、この二つの言語環境は一致している。
    • ターミナルの言語環境を設定する時に「起動時にロケール環境変数を設定=チェックあり」にしているので。


  • ところが、今回のようにターミナルの言語環境を切り替え、ターミナルを再起動せずに使っていると、不一致な状態に陥る。
    • 例えば、ターミナルで先ほど開いた日本語(Shift JIS)のタブに戻ってみる。
    • 現在のターミナルの言語環境は、ユニコードUTF-8)である。
    • ところが、コマンド実行環境としてのロケールはShift-JISのままなのだ。
$ locale
LANG="ja_JP.SJIS"
LC_COLLATE="ja_JP.SJIS"
LC_CTYPE="ja_JP.SJIS"
LC_MESSAGES="ja_JP.SJIS"
LC_MONETARY="ja_JP.SJIS"
LC_NUMERIC="ja_JP.SJIS"
LC_TIME="ja_JP.SJIS"
LC_ALL=
  • この状態だと、先ほどは正常に表示されたencoding_test.applescriptも、文字化けすることになる。
$ cat $HOME/Library/Scripts/encoding_test.applescript
tell application "Finder"
	display dialog "????΂??"
end tell
  • つまり、最終的には3つの言語環境を一致させておく必要があるのだ。
言語環境の区分 意味
テキストファイル ファイルに記録されているエンコード方式
コマンドロケール コマンド処理する時に解釈されるエンコード方式
ターミナル言語環境 画面出力する時に解釈されるエンコード方式
テキストファイル コマンドロケール ターミナル言語環境 出力結果 判定
Shift JIS Shift JIS Shift JIS こんばんは OK
Shift JIS Shift JIS UTF-8 ????΂?? 文字化け
Shift JIS UTF-8 UTF-8 ????΂?? 文字化け
UTF-8 UTF-8 UTF-8 こんばんは OK
UTF-8 UTF-8 Shift JIS 縺薙s縺ー繧薙� 文字化け
UTF-8 Shift JIS Shift JIS 縺薙s縺ー繧薙� 文字化け

ロケールの影響を受けるコマンド、受けないコマンド

  • ところで、コマンドには、ロケールの影響を受けるものと、受けないものがある。
  • 例えば、catコマンドはどんなロケールだろうと、忠実にテキストファイルの文字コードをそのまま読み取り、そのまま出力する。
  • よって、以下のようなロケールであっても、正常に表示されるのだ。
テキストファイル コマンドロケール ターミナル言語環境 出力結果 判定
Shift JIS UTF-8 Shift JIS こんにちは OK
$ cat $HOME/Library/Scripts/encoding_test.applescript
tell application "Finder"
	display dialog "こんばんは"
end tell
  • ところが、odコマンドを実行すると、このようになる。
$ od -tx1c $HOME/Library/Scripts/encoding_test.applescript
0000000    74  65  6c  6c  20  61  70  70  6c  69  63  61  74  69  6f  6e
           t   e   l   l       a   p   p   l   i   c   a   t   i   o   n
0000020    20  22  46  69  6e  64  65  72  22  0a  09  64  69  73  70  6c
               "   F   i   n   d   e   r   "  \n  \t   d   i   s   p   l
0000040    61  79  20  64  69  61  6c  6f  67  20  22  82  b1  82  f1  82
           a   y       d   i   a   l   o   g       " 202 261 202 316 202
0000060    ce  82  f1  82  cd  22  0a  65  6e  64  20  74  65  6c  6c    
         316 202 361 202 315   "  \n   e   n   d       t   e   l   l    
0000077
  • 「こんばんは」と表示されずに、8進数文字コードで表示されている。
  • 3つすべての言語環境が一致していれば、本来は「こんばんは」と表示されるはず。
$ od -tx1c $HOME/Library/Scripts/encoding_test.applescript
0000000    74  65  6c  6c  20  61  70  70  6c  69  63  61  74  69  6f  6e
           t   e   l   l       a   p   p   l   i   c   a   t   i   o   n
0000020    20  22  46  69  6e  64  65  72  22  0a  09  64  69  73  70  6c
               "   F   i   n   d   e   r   "  \n  \t   d   i   s   p   l
0000040    61  79  20  64  69  61  6c  6f  67  20  22  82  b1  82  f1  82
           a   y       d   i   a   l   o   g       "   こ  **   ん  **   ば
0000060    ce  82  f1  82  cd  22  0a  65  6e  64  20  74  65  6c  6c    
          **   ん  **   は  **   "  \n   e   n   d       t   e   l   l    
0000077

ロケールの設定方法

  • 今まで、ターミナルの設定で「起動時にロケール環境変数を設定=チェックあり」に頼っていた。
  • でも場合によっては、ロケールのみを個別に設定したい状況もあり得る。
  • また、ターミナル以外の実行環境でコマンドを実行する場合も結構ある。
  • そんな時は、コマンドを使って、自分でロケールを設定し直す必要がある。
設定可能なロケール一覧
$ locale -a
  • すべてのロケールがずらっと表示されてしまうので、日本語のロケールのみ表示してみる。
$ locale -a | grep ja_JP
ja_JP
ja_JP.eucJP
ja_JP.SJIS
ja_JP.UTF-8
ロケールを設定する
  • ロケールはLANG変数に設定して、exportする必要がある。
$ export LANG=ja_JP.UTF-8

$ locale
LANG="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_CTYPE="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_ALL=
  • LANG変数が設定されていないと、英語環境になるのだと思う。
$ unset LANG

$ locale
LANG=
LC_COLLATE="C"
LC_CTYPE="C"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=


AppleScriptAutomatorでテキスト処理をするなら、export LANG=ja_JP.UTF-8を設定しておくと幸せになる。

文字コードを変換する方法

  • コマンド環境で文字コードを変換するには、自分は現状nkfコマンドに頼りきっている。
  • nkfコマンドはOSX標準インストールされないのだけど、テキストのエンコード方式を自動判定してくれるところが、たいへん便利。
  • 様々な変換に対応しているが、自分はもうnkf -wばかり使っている。(UTF-8へ変換)
$ cat $HOME/Library/Scripts/encoding_test.applescript | nkf -w
tell application "Finder"
	display dialog "こんばんは"
end tell

$ cat $HOME/Documents/encoding_test.txt | nkf -w
tell application "Finder"
	display dialog "こんばんは"
end
  • 入力がどのようなテキストエンコード方式であっても、UTF-8に変換してくれるのだ。
  • 改行コードを変換するオプション-Luと組み合わせれば、素晴らしく便利な使い心地だ。


OSX環境でテキスト処理をするのなら、nkf -w -Luを覚えておく価値がある。