第4回のカテゴリーは「Perl使いのための文字コード入門」です。
 『かんたんPerl』では、普通のプログラミング入門書に比較すると多くの紙数を費やして文字コードのことを書いています。特に第10章「日本語処理」では、非常に便利でありながらハマっている方が多いと思われるEncodeモジュールの基本の使い方についてコッテリ研究しましたし、WindowsとUNIXで間違いなく両方動くスクリプトをUTF-8で書く方法を研究しました。
 これは、ぼくが文字コードの話をするのが好きだからというだけではなく、じっさいコンピューターを使うとき最初に、そしていつまでも遭遇するトラブルが文字化けだからです。
 ということで、このブログ カテゴリーでは、『かんたんPerl』読者が遭遇するであろう文字コード トラブルと、それを解決するために事前に知っておきたい文字コードの用語、概念、そして文字コードをハンドリングするためにPerlに用意された機能を紹介していきたいと思います。
第1回の今回は、まず文字コードって何、文字化けって何、という話をします。

 コンピューターの中では、数値、文字を初め、画像、音声などのあらゆるデータが数値に変換されて入っています。この数値のことをコード(code、符号)といいます。ここでは文字のことに話をしぼりますが、キーボードから入力した文字をパソコンに格納するために、文字からコードに変換することをエンコード(encode、符号化)、パソコンに格納した文字を画面に表示するために、コードから文字に変換することをデコード(decode、復号化)と言います。

20151230_mojicode1

 上図はサクラエディタを使って、「ABC」という文字列をASCIIという世界最古の文字コードで入力してテキスト ファイルABC.txtを作ったところです。(画面にはLatin-1と書かれていますが、今は気にしないでください。)
 文字コードを知るためには、テキスト ファイルABC.txtをダンプします。ここではWindowsのダンプ ツールxdumpを使ってみます。
 ダンプ ツールを見ると、Aが0x41、Bが0x42、Cが0x43だと分かります。
 0x41は16進数で、ヘキサのヨンイチと読みます。コンピューターの中の数字は2進数で、ABCは01000001b 01000010b 01000011bとなっていますが(2進数を書くときにはこのようにbを付けることがあります。binaryの略です)、見づらいので4ケタずつ16進数に束ねて0x41、0x42、0x43と書きます。
 つまり、Aをエンコードすると0x41に、0x41をデコードするとAになることなります。
 で、Aと0x41の対応関係をエンコーディング スキーム(文字コード系)と言って、沢山の種類があります。ABCを0x414243と対応させるエンコーディング スキームがASCII(アスキー)で、コンピューター界で最古のエンコーディング スキームです。

 エンコーディング スキームは膨大な文字の対応関係を定めているので、コード表が必要です。『かんたんPerl』では電子版の文字コード表、wincode1_mod.txtとwincode2_mod.txtをサンプル ファイルとともに提供しています。(でも、ASCIIのコード表ぐらい「ASCII コード表」で検索するとすぐ見つかりますね。)

 上図はwincode1_mod.txtの一部をxyzzyというエディターで開いてみているものです。「字」という欄に文字が、「SJIS」という欄にASCIIコードが載っています。なぜASCIIなのにSJISかもおいおい説明します。ここではABCが41 42 43という文字に対応していることが、電子コード表ですぐ分かるところに注目してください。
20151230wincode1



 さて、上では、サクラエディタ、xdump、xyzzyといったWindowsのアプリや、電子コード表wincode1_mod.txtを使って文字コードについて研究しました。では、ここからはPerlを使って研究します。

 ABCという文字のコードを知るには、『かんたんPerl』にもちょこっと出てきましたがunpack関数を使います。
 これは、バイナリー状態に詰め込まれているデータを解きほぐしてコードの中身を知る、という関数です。

#! /usr/local/bin/perl
# testUnpack.pl -- unpack関数で文字コードを知る

use strict;
use warnings;
use 5.010;

say "A => ", unpack("H2", "A");
say "B => ", unpack("H2", "B");
say "C => ", unpack("H2", "C");

 実行してみます。

[sample]$ ./testUnpack.pl
A => 41
B => 42
C => 43

 オッケーですね。unpackによってAという字がH2つまり16進数(Hexadecimal)2ケタに解釈されて、41という16進数が表示されました。
 ではこの逆に、0x41、0x42、0x43という数値を得て、それらのコードをデコードするとどのような字が得られるか知るにはどうすればいいでしょうか。
 天才的なあなたはお察しの通り、デコードにunpackを使ったのだからエンコードはpackを使えばいいのではないでしょうか。

#! /usr/local/bin/perl
# testPack.pl -- pack関数で文字を知る

use strict;
use warnings;
use 5.010;

say "0x41 => ", pack("H2", "41");
say "0x42 => ", pack("H2", "42");
say "0x43 => ", pack("H2", "43");

 さあどうだ。

[sample]$ ./testPack.pl
0x41 => A
0x42 => B
0x43 => C

 あはは、ちゃんと出来ましたね。pack、unpackは超奥が深い関数ですが、やりたいことを整理して少しずつ練習していけばいいと思います。とりあえずpackとunpackは対になっていることがよく分かりますね。
 さて、ABCとPerlで表示するには、say "ABC"と言えばいいんですが、もじABCの文字コードだけは入力できるが、ABCという文字列だけはどうしても入力したくないという状況に陥ったとき、どうすればいいでしょうか。
 実は二重引用符の中で\x??というエスケープ文字を書けば、0x??という文字コードを表示してくれます。16進数2ケタしか使えないところに注意してください。よって

#! /usr/local/bin/perl
# testHex.pl -- \x??記法で文字を出力する

use strict;
use warnings;
use 5.010;

say "Jackson 5's hit tune is \x41\x42\x43.";

というコードを書くと、

[sample]$ ./testHex.pl
Jackson 5's hit tune is ABC.

と動作します。いや、ABCぐらいちゃんと

say "Jackson 5's hit tune is ABC.";

と書くべきですが、打ち込みづらい文字でも16進数2ケタずつ書けば書ける、というのは、役に立つ局面もあるでしょう。

【今日のまとめ】
  • 文字はコンピューターの中で数値になっている。この数値を文字コードという
  • 文字をコードに変換することをエンコードという
  • コードを文字に変換することをデコードという
  • コードと文字の対応関係をエンコーディング スキームという
  • エンコーディング スキームにはいろいろある
  • 世界最古の文字コード系がASCII(アスキー)
  • ASCIIに則って「ABC」をエンコードすると「0x414243」になる
  • ここで0x??とは2ケタの16進数のことである。
  • コンピューターの中では01000001bのような2進数が入っているが、桁数が多くて人間にはつらいので0x41と2進数4ケタを16進数1ケタにまとめる
  • エンコーディング スキームはコード表に載っている
  • ファイルからコードを知るにはダンプ ツールを使う
  • Perlで文字から文字コードを知るにはunpack関数を使う
  • Perlで文字コードから文字か知るにはpack関数を使う
  • エスケープ文字\x??を二重引用符の中に書くと、0x??にコードされた文字が表示される
いかがですか。今日やったたったこれだけのことの中に、これだけ知識が入っています。
この調子で書いていると、漢字とか絵文字とかUnicodeとかEncodeモジュールとかを自由自在に使いこなせるようになるのはいつのことか途方に暮れますが、ま、徐々にやっていきましょう。