漢字コードに関する基本的な知識を確認しておきたいと思います。下記に、"perl入門!!" という文字列を JISコード、EUC、シフトJIS、Unicode で表したときの文字コードを16進数で示します。
p | e | r | l | (KI) | 入 | 門 | (KO) | ! | ! | |
---|---|---|---|---|---|---|---|---|---|---|
JIS | 70 | 65 | 72 | 6c | 1b 24 42 | 46 7e | 4c 67 | 1b 28 42 | 21 | 21 |
EUC | 70 | 65 | 72 | 6c | c6 fe | cc e7 | 21 | 21 | ||
SJIS | 70 | 65 | 72 | 6c | 93 fc | 96 e5 | 21 | 21 | ||
Unicode | 00 70 | 00 65 | 00 72 | 00 6c | 51 65 | 95 80 | 00 21 | 00 21 |
それぞれの文字コードの特徴について説明します。
ASCII | 半角カタカナを除く半角英数記号文字です。文字コードは 0x00~0x7f の範囲にあります。詳細は ASCIIコード一覧 を参照してください。 |
---|---|
JISコード | 日本語の前後に KI(漢字IN)と呼ばれるコードと、KO(漢字OUT)と呼ばれるコードが入ります。KI は、1b 24 42 または 1b 24 40 です。KO は 1b 28 42 または 1b 28 4a が使用されています。漢字は2バイトで、1バイト目、2バイト目共に 21~7e の範囲になります。 |
EUC | KI や KO はありません。基本的に、JISコードの1バイト目と2バイト目の最上位ビットを 1 にしたもの(0x80 を足したもの)が EUC になります。2バイト目が ASCII コードと重複しないので、プログラム的には一番扱いやすいコードです。 |
シフトJIS | KI や KO はありません。JISコードに対して単純な演算によりコンバート可能です。1バイト目は 81~9f または e0~fe となり、最上位ビットは1です。2バイト目は 40~7e または 80~fc で、ASCII コードと重なる部分があります。 |
半角カナ | シフトJIS と共によく用いられる 1バイトのカタカナコードです。a1~df の範囲にあります。シフトJIS は、この半角カナのコードを避けるように定義されています。 |
Unicode | 世界中の文字をひとつの文字コード体系で表そうとする、比較的新しい文字コードです。基本的には 1文字を 2バイトで表しますが、これをファイルに格納したりネットワークに流す場合は、UTF-8 などでさらにエンコードします。UTF-8 では、ASCII 領域の文字は 1バイトに、漢字領域の文字は 3バイトにエンコードします。Unicode の変換には Jcode.pm などを利用します。 |
現在、perl で日本語コードを扱うには、現状、EUC で処理するのが一番おすすめです。
漢字コードの変換を行うには、下記のサイトから歌代氏が開発された jcode.pl というライブラリを入手しておくと便利です。現在の最新版は 2.13 のようです。
入手したファイルは jcode.pl-2.13 のようなファイル名になっていますが、改行コードが自OSに適したものになっていない場合があります。例えば、次のようにして改行コードとファイル名を変換しておいてください。
perl -p -e 's/[\r\n]*$/\n/' jcode.pl-2.13 > jcode.pl
jcode.pl を使用するには、スクリプトの先頭の方で jcode.pl ライブラリを読み込まなくてはなりません。ライブラリの読み込みには require を用います。
require "jcode.pl";
jcode.pl はいくつかの機能を含んでいます。その中の一つ、漢字コードを調べる getcode() を呼び出すには次のようにします。
$code = &jcode'getcode(*str); # Perl 4 的な呼び出し方
Perl 5 では、上記のようにも記述できますが、下記のように記述することも多いようです。
$code = jcode::getcode(\$str); # Perl 5 的な呼び出し方
jcode.pl の実際の使用方法については次ページ以降で紹介します。
jcode::getcode() は、入力した文字列の漢字コードを調べます。ASCII のみの場合は特別な値 undef を、シフトJIS のときは "sjis"、EUC のときは "euc"、JIS のときは "jis"、バイナリデータと思われる場合は "binary" を返します。
require "jcode.pl"; $str = "日本語"; if ($code = jcode::getcode(\$str)) { print "code = $code\n"; } else { print "code = ascii\n"; }
シフトJIS と EUC はコードが重なっている部分があるため、識別できない場合があります。特に半角カタカナを用いた文字列は識別できない可能性が高まります。例えば下記のコードをシフトJISのスクリプトとして作成してください。
require "jcode.pl"; $str = "ひらがなアイウエオ"; $code1 = jcode::getcode(\$str); $str = "ひらがアイウエオ"; $code2 = jcode::getcode(\$str); $str = "ひらアイウエオ"; $code3 = jcode::getcode(\$str); print "code1=$code1 code2=$code2 code3=$code3\n";
結果は次のようになってしまいます。
code1=sjis code2= code3=euc
※ これは jcode.pl の責任ではありません。EUC とシフトJIS のコードが重なっているため、どうやっても識別できないのです。
jcode::convert() は、入力された文字列の漢字コードを自動的に判別して、指定した漢字コードに変換します。第2引数には、"sjis"、"euc"、"jis" のいずれかを指定します。例えば、入力された文字列のコードを自動判別して、これを EUC に変換するには次のようにします。
require "jcode.pl"; $str = "日本語"; jcode::convert(\$str, "euc"); print "$str\n";
入力する漢字コードが判明している場合は、第3引数で入力漢字コードを指定することができます。これにより、漢字コードの誤判断を防ぐことができます。
# sjis → euc の変換 jcode::convert(\$str, "euc", "sjis");
jcode::xxx2yyy() の形式のサブルーチンを使用することも可能です。例えば下記の例では、シフトJIS から EUC への変換を行います。
require "jcode.pl"; $str = "日本語"; jcode::sjis2euc(\$str); print "$str\n";
半角文字を全角文字に変換するには jcode::tr() を用います。シフトJISを扱う場合は "A-Z" のような指定ができないため、下記のようにすべての文字を並べます。ハイフン(-)は最後に記述する必要があります。全角を半角にするには、$from と $to を入れ替えます。
require "jcode.pl"; $from = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" . "0123456789!\"#\$\%&'()*+,./:;<=>?\@[\\]^_`{|}~-"; $to = "abcdefghijklmnopqrstuvwxyz" . "ABCDEFGHIJKLMNOPQRSTUVWXYZ" . "0123456789" . "!”#$%&’()*+,./:;<=>?@[¥]^_‘{|}~-"; $str = "This is Japan."; jcode::tr(\$str, $from, $to); print "$str\n";
jcode::convert() の第4引数に "z" を指定すると半角カタカナ→全角カタカナの変換が行われます。"h" を指定するとその逆になります。入力漢字コードを自動判別する場合は 第3引数に "" を指定することができますが、半角カタカナ混じりの文章は自動判別が誤認識を行う可能性が高いので注意してください。
require "jcode.pl"; $str = "ハンカク文字は大丈夫かな?"; jcode::convert(\$str, "sjis", "", "z"); print "$str\n";
結果は次のようになります。
ハンカク文字は大丈夫かな?
漢字混じりの文字列を1文字ずつ処理します。このサンプルでは、漢字コードが EUC または半角カタカナを含まないシフトJISであることを期待しています。[\x00-\x7f]|.. は、コードが 0x00~0x7f の範囲であれば 1バイト分の文字に、さもなくば 2バイト分の文字にマッチします。
$str = "This is 日本."; $len = 0; while ($str =~ /([\x00-\x7f]|..)/g) { $len++; } print "len = $len\n";
結果は次のようになります。
len = 11
半角カタカナを含むシフトJISの場合は、[\x00-\x7f] を [\x00-\x7f\xa1-\xdf] としてください。
漢字混じりの文字列を1文字ずつ処理します。このサンプルでは、漢字コードが EUC または半角カタカナを含まないシフトJISであることを期待しています。
$str = "This is 日本."; while ($str =~ /([\x00-\x7f]|..)/g) { print "[$1] "; } print "\n";
結果は次のようになります。
[T] [h] [i] [s] [ ] [i] [s] [ ] [日] [本] [.]
半角カタカナを含むシフトJISの場合は、[\x00-\x7f] を [\x00-\x7f\xa1-\xdf] としてください。
ActivePerl でスクリプトを作成するときなど、スクリプトに記述した文字が 文字化け してしまうことがあります。これは、シフトJIS の漢字コードの 2バイト目がバックスラッシュ(\)と同じ 0x5c になってしまうため、perl がこれを特殊文字と判断してしまうことにより発生します。
print "表示できるかな\n";
シフトJIS で記述された上記スクリプトを実行すると、次のように表示されてしまします。
侮ヲできるかな
これを防ぐには、漢字コードを EUC にしてスクリプトを書くのが効果的です。Windows でも、漢字コードを EUC で保存できるテキストエディタがいくつかあるようです。また、転送時にシフトJIS → EUC に変換が可能な FTP ソフトもあるようです。
EUC で記述するのが困難な場合は、2 バイト目が 0x5c となる文字の直後に \ を余分に書き足すことにより回避できます。2 バイト目が 0x5c となる文字は、「表」の他に、「ソ」、「十」、「申」、「予」、「噂」、「構」、「貼」、「暴」、「能」などがあります。
print "表\示できるかな\n";
こうすると、ちゃんと次のように表示されます。
表示できるかな
上記以外の原因で文字化けが発生するケースがあります。例えば、ブラウザが文字コードの誤判別を行ってしまった場合については「CGIの結果が文字化けしないようにする」を参照してください。
Jcode.pm は、Jcode.pl の後継ともいうべき モジュール です。Jcode.pl の機能をモジュールベースに変更したのに加え、Unicode や MIME-encoding などの機能もサポートされています。下記から入手可能です。
UNIX 環境では CPAN を利用することができます。インストールにはスーパーユーザーの権限が必要です。
# perl -MCPAN -e shell cpan> install Jcode
ソースコードを自分でコンパイルする場合は、下記のようにしてください。
# tar xvfz Jcode-0.80.tar.gz # cd ./Jcode-0.80 # perl Makefile.PL # make # make test # make install
Windows の場合、Jcode-0.80.zip を入手、解凍し、以下の手順でインストールしてください。ppm にも登録されています。
C:\> cd Jcode-0.80 C:\Jcode-0.80> perl win_install.pl
jcode.pl を用いた jcode3.pl を、Jcode.pm を用いた方法に書き直すと次のようになります。日本語を EUC に変換しています。
use Jcode; $str = "日本語"; Jcode::convert(\$str, "euc"); print "$str\n";
Jcode.pm では、次のような書き方もできます。下記の例では、文字列を h2z() で半角カタカナから全角カタカナに変換し、さらにそれを sjis() でシフトJISに変換しています。
use Jcode; $str = "日本語"; $str = Jcode->new($str)->h2z->sjis; print "$str\n";
Jcode->new($str) は単に jcode($str) と記述することもできます。
$str = jcode($str)->h2z->sjis;
第二引数には入力漢字コードを指定することができます。
$str = jcode($str, "sjis")->h2z->sjis;
UCS-2 は、Unicode の 2バイト文字をほとんどそのまま表現する方式です。シフトJIS を UCS-2 形式の Unicode に変換するには次のようにします。
use Jcode; $sjis = "日本語"; $ucs2 = jcode($sjis, "sjis")->ucs2; for ($i = 0; $i < length($ucs2); $i++) { printf("%02x ", ord(substr($ucs2, $i, 1))); } print "\n";
結果は次のようになります。日本語 1文字が 2バイトで表現されています。
65 e5 67 2c 8a 9e
UTF-8 は、Unicode の 2バイト文字をさらに、ASCII コードは 1バイトに、日本語などのコードは 3バイトなどに変換して表現する形式です。シフトJIS を UTF-8 形式の Unicode に変換するには次のようにします。
use Jcode; $sjis = "日本語"; $utf8 = jcode($sjis, "sjis")->utf8; for ($i = 0; $i < length($utf8); $i++) { printf("%02x ", ord(substr($utf8, $i, 1))); } print "\n";
結果は次のようになります。日本語 1文字が 3バイトで表現されています。
e6 97 a5 e6 9c ac e8 aa 9e
Internet Explorer の JavaScript の escape() メソッドを用いると、"日本語" といった全角文字が "%u65E5%u672c%u8A9E" のような形式の Unicode にエンコードされます。これをデコードしてシフトJISに変換するには次のようにします。
use Jcode; $ucs2 = "%u65E5%u672c%u8A9E"; $sjis = $ucs2; $sjis =~ s/%u([0-9A-Fa-f]{4})/ucs2sjis($1)/eg; print "$sjis\n"; sub ucs2sjis { local($arg) = @_; local($u); $u = chr(hex(substr($arg, 0, 2))) . chr(hex(substr($arg, 2, 2))); jcode($u, "ucs2")->sjis(); }
結果は次のようになります。
日本語
UTF-8 形式のバイナリデータをシフトJISに変換するには次のようにします。
use Jcode; $utf8 = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; $sjis = jcode($utf8, "utf8")->sjis; print "$sjis\n";
結果は次のようになります。
日本語