今回は前回説明したtextutilを使って文字コードを変換します。macOSにはもう1つ文字コードを変換するiconvがありますので、合わせて説明します。なお、文字コードは大変に複雑・面倒なので怪しい説明になっている面もあるかもしれません。また、機種・OS固有の文字コードの問題に加えてアプリケーション側での問題もあります。説明が怪しいなと疑問に思われた方はWebで調べてみてください。

今回もこれまでのようにサンプルで利用するファイル・ディレクトリはデスクトップのsampleディレクトリとしています。デスクトップにsampleディレクトリがない場合は作成しておいてください。(コマンド入力ならmkdir ~/Desktop/sampleとして作成することができます)
また、カレントディレクトリも上記の場所になります。cd ~/Desktop/sampleのようにコマンドを入力してカレントディレクトリを変更しておけばよいでしょう。

textutilで文字コードを変換する

 それではtextutilを使って文字コードの変換を行ってみましょう。文字コードを変換するツール・コマンドとしてはnkfが有名ですが、Macではiconvコマンドが用意されているので、そちらを使った方が楽でしょう。iconvがあるのになぜtextutilで?という人もいるでしょう。そこはそれ、ワード文書などに直接変換できるということで許してください。iconvについては後ほど説明します。

 その前に「文字コードって何?」という人もいるかもしれません。すでにUTF-8がスタンダードになって相当の年数が経過し絵文字も使えるようになったので、文字コードに悩まされることは、かなり減ったのではないかと思います。UTF-8以外の文字コードで多く使われたのは日本国内ではWindowsやPC-9801や古いMacで使われていたShift JISがあります。機種ごとに微妙に空き領域で使用している文字コードが異なります。次にUNIX系で使われていたEUC(EUC-JP)です。古いUNIX系の文書ではEUCの文字コードとなっている場合もあります。他にはJISなどの文字コードもあります。
 文字コードに応じて処理できない場合は文字化けが発生することがあります。使用しているエディタやアプリケーションにもよりますが、古くからの慣れた人(?)であれば、どう文字化けしているかによって、ある程度文字コードを推測することができるでしょう。

 ここではShift JISの文字コードを使ったファイルとして1sjis.txtを、EUC-JPの文字コードを使ったファイルとして2euc.txtを用意しました。中身は、この連載の先頭部分です。

textutilでは-inputencodingの後に変換前のエンコーディング名を、-encodingの後に変換後のエンコーディング名を指定します。指定できるエンコーディング名は以下のサイトで確認してください。なお、以下のページには記載がありませんが、Shift JISはSJISのように省略して指定できます。

・IANA Character Sets
https://www.iana.org/assignments/character-sets/character-sets.xhtml

 それでは1sjis.txtをUTF-8の文字コードに変換して5.txtというファイル名で保存してみましょう。この場合、以下のように指定します。

textutil -inputencoding Shift_JIS -convert txt 1sjis.txt -encoding UTF-8 -output 5.txt

 無事に変換されると5.txtが生成されます。catコマンドで内容を確認すると1sjis.txtとは異なり文字化けせずに表示されているのがわかります。

 同様に2euc.txtはEUC-JPの文字コード名を指定すれば変換できます。

textutil -inputencoding EUC-JP -convert txt 2euc.txt -encoding UTF-8 -output 6.txt

iconvで文字コードを変換

 せっかくなのでおまけでiconvを使った場合の文字コード変換例も説明しておきます。なお、OSのバージョンによってはiconvで期待通りに文字が変換されないことがあります。正規化されてると思ったら正規化されていなくて正気か?みたいなことがあるので注意してください。人名や地名などを変換する場合は要注意です。
 まず、Shift JISコードの1sjis.txtファイルをUTF-8コードの5.txtに変換する場合は以下のようになります。-fの後に変換前の文字コードを、-tの後に変換後の文字コードを指定します。変換したテキストは標準出力に流されるのでリダイレクトを使ってファイルに保存します。

iconv -f SJIS -t UTF-8 1sjis.txt >5.txt

 次にEUC-JPの文字コードの2euc.txtをUTF-8コードの6.txtに変換する場合は以下のようになります。

iconv -f EUC-JP -t UTF-8 2euc.txt >6.txt

 ちなみにiconvで変換できるエンコードの種類はiconv -lとすると表示されます。

 この対応した文字コードの中で見慣れないUTF-8-MAC UTF8-MACというものがあります。

iconvでも以下のように指定してUTF-8-MACからUTF-8などの文字コードに変換できます。

iconv -f UTF-8-MAC -t UTF-8 input.txt

 この見慣れないUTF-8-MACですが、Googleでこのキーワードを入れて検索するといろいろ出てきます。いろいろ問題はあるようですが、目立つ物としてはMacではアプリケーションによって濁点が分離してしまったりするようです。例えば以下のような4文字があったとします。

ガルパン

 これは普通に見ると日本語のカタカナ4文字です。ところがMacでは以下のように濁点等が分離しているので6文字扱いになってしまいます。(ちょうどいい文字がないので以下の表示は、それらしいイメージとして提示しています)

カ”ルハ。ン

 ここで困るのは内部では分離しているのにFinderなどでは「カ"」は「ガ」に変換されて表示されているということです。

つまり見た目には同じ文字が表示されているので文字列検索・ファイル名検索しているのに、なぜかヒットしない(検索結果に出てこない)ということになります。ここらへんはなかなか難しくて一筋縄ではいかないところです。WindowsからMacへ持ってくる場合は不具合は少なそうですが、逆はかなり不具合が発生することがあります。そればかりか古いMacから最新のMacにファイルをコピーするだけでも、ファイル名に不具合が発生することもあります。(10.6→11.6など)

ダンプリストでチェック

 見た目は同じなのに中身が違う場合には差分をとって調べる方法もあります。diffを使えば見た目の文字が同じでも文字コードを示す内部の値が異なるのでチェックすることができます。

しかし、テキストでの比較なので違うのはわかるにしても、どこの値が違うのかまではわかりません。そんな時にはダンプリストを表示して確認するという方法があります。
ダンプリストはファイルの中身を16進数で示すものです。hexdumpというコマンドを使えばダンプリストを出力してくれます。使い方は簡単でhexdumpの後に16進数で出力したいファイル名(ファイルパス)を指定するだけです。
 ここではtestUTF-Mac2.txtとtestUTF-Mac3.txtのファイルを用意しました。testUTF-Mac2.txtはMacのテキストエディタでガルパンの文字を入力したもので、testUTF-Mac3.txtはFinderでファイル名をコピーしたものです。いずれも古いMacを使って作成しています。

 それでは2つのファイルの中身をhexdumpを使って表示してみましょう。

 テキストエディタでは同じ見た目なのに内部では値が異なっています。多くのプログラム/アプリケーションでは文字コードの情報まで考慮して処理することはしません。このため、grepなどで検索してもヒットしないのは仕方ないのかもしれません。1980年代の経緯からすれば、こんな面倒なことにならず綺麗に(シンプルに?)文字コードが統一される気もしましたが、現実はUnicodeになっても複雑なまま今に至っているようです。ここらへんは、もうどうしようもないでしょうから、何かおかしいなと思ったら文字コードが怪しいのではないか?と疑ってみるのもありでしょう。

xxdコマンド

 16進数で中身を表示するコマンドとしては他にxxdがあります。これも使い方は簡単でxxdの後に中身を表示したいファイル名(ファイルパス)を指定します。testUTF-Mac2.txtとtestUTF-Mac3.txtのファイルをxxdコマンドで表示すると以下のようになります。hexdumpとは表示結果が異なります。右側の...の部分には可読可能であれば文字が表示されます。

ちなみに16進数でなく8進数で出力する場合はodというコマンドがあります。

 xxdコマンドは内容を16進数で出力するだけでなく逆もできます。つまり16進数値からバイナリデータに戻すことができます。この場合、-rと-pを同時に指定しておきます。
 例えばABCの文字を示す値(アスキーコード)から文字に戻すには以下のようにします。

echo '414243' | xxd -r -p
・アスキーコード
https://en.wikipedia.org/wiki/ASCII

 せっかくなのでxxdを使って、先程のガルパンの文字を復元してみましょう。hexdumpで表示された文字を使います。以下の文字コードはtestUTF-Mac2.txtのものです。

echo 'efbbbfe382ace383abe38391e383b3' | xxd -r -p

 testUTF-Mac3.txtの文字コードの方もやってみましょう。こちらも問題なく復元できます。

echo 'efbbbfe382abe38299e383abe3838fe3829ae383b3' | xxd -r -p

 ここで1つ実験をしてみましょう。上記のUTF-8-MACでは濁点が分離しています。UTF-8での濁点の文字コードはe38299です。

・Unicode Character 'COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK' (U+3099)
https://www.fileformat.info/info/unicode/char/3099/index.htm

 先程復元した文字から最初のガの濁点だけ削除してみます。つまり以下のようにするとカルパンと表示されます。

echo 'efbbbfe382abe383abe3838fe3829ae383b3' | xxd -r -p

濁点を連続させるなら濁点のコードを増やします。が、このように変更した場合ターミナルやエディタで正常に表示されるとは限りません。

echo 'efbbbfe382abe38299e38299e383abe3838fe3829ae383b3' | xxd -r -p

濁点を半濁点に変更することもできます。半濁点のUTF-8のコードはe3829cなので38299の部分を変更します。

・半濁点
https://www.fileformat.info/info/unicode/char/309c/index.htm
echo 'efbbbfe382abe3829ce383abe3838fe3829ae383b3' | xxd -r -p

 16進数値からバイナリデータを生成しましたが、バイナリファイルとして保存したい場合はリダイレクトを使います。以下のようにするとカレントディレクトリにa.txtというファイル名で保存されます。

 文字コードは歴史的な経緯もあって、なかなか難しいのですが、興味を持たれたら、いろいろ探求してみるのもよいでしょう。本連載では深入りせずに、このくらいにとどめておきます。

著者 仲村次郎
いろいろな事に手を出してみたものの結局身につかず、とりあえず目的の事ができればいいんじゃないかみたいな感じで生きております。