文字化け対応の指針
さて、日本語の文字化けにはざっと上記の3種類があります。あるものは不可避ですが、プログラマー的にはなるべく問題が起きないように努力したいところですよね。残念ながら全てを解決することはできないものの、読めなくなる問題を避けるため、いくつかの指針を設けることができます。
入力を避ける
例えば、入力されたデータを後でメールで送信することが分かっているのであれば、ISO-2022-JP に含まれない文字は入力の段階でエラーとしてしまう対応が考えられます。後で Shift_JIS (または CP932) で CSV ファイルにしたい場合も同様です。 (もちろん、メールも CSV も UTF-8 での運用もあり得ます。その場合は特定の文字の文字化けの心配は無くなります。色々な可能性を検討して下さい。)
また、CSV (Shift_JIS系文字コード) を Mac と Windows の両方で文字化けなく読めるようにしたい場合も、MacJapanese と CP932 で共通していない文字の入力を避ける必要があります。
異なる文字コード間で共通でない文字のチェック方法
どの文字が共通していないか、文字コードの仕様を調べて正規表現で制限することもできますが、面倒ですね。この場合は以下のように「一旦他の文字コードに変換して逆変換で戻した時に同じ文字列になるか」をチェックすると簡単です。
use strict; use warnings; use utf8; # 文字化けを起こさないかチェックする # 引数:文字列(内部表現)、使いたい文字コード(Perl表記)の配列参照 # 戻り値:1=OK、0=NG sub is_safe_str{ my ($str, $encref) = @_; #文字列が指定されていない場合はOK defined $str and length $str or return 1; #文字コードが指定されていない場合はOK defined $encref and ref $encref eq 'ARRAY' and scalar @$encref or return 1; my $roundtrip = encode($encref->[0], $str); for (my $i=0; $i<scalar @$encref; $i++){ #順番にコード変換し、最後にUTF-8に戻す(生文字列) from_to($roundtrip, $encref->[$i], $encref->[$i+1]||'utf8'); } decode('utf8', $roundtrip) eq $str and return 1 or return 0 ; } &is_safe_str('波線~です', [qw(MacJapanese cp932 iso-2022-jp)]) and print "OK\n" or print "NG\n" ;
変換する
よく使われる機種依存文字の中には、文字化けしない他の表現に変更できるものがあります。例えば、UTF-8で入力した「①」 (マル1、U+2460、e291a0) は CP932 に変換した時に 8740 に変換され、これはMacで見ると「(日)」になる場合があります。このような文字の場合、例えば「マル1」は「(1)」など、代替表現に変換してしまえば文字化けを避けることができます。
このような変換可能な文字は例えば、マル付数字、㈱、㈲、㎡、等があります。以下のコードで変換できます。
use strict; use warnings; use utf8; # 文字化けを起こす可能性のある文字を変換する # 引数:文字列(内部表現) # 戻り値:文字列(内部表現) sub conv2safe_expression{ my $str = shift; defined $str and length $str or return $str; #変換設定 my @expressions = map {[split(/,/, $_, 2)]} qw( ①,(1) ②,(2) ㈱,(株) ㈲,(有) ㎡,平方メートル ); #必要なだけ足す。多い場合は外部ファイルにすると分かり易い。 for my $e (@expressions){ $str =~ s/$e->[0]/$e->[1]/g; } return $str; } print &conv2safe_expression('38.23㎡'); # 38.23平方メートル
エンティティで表現する (HTMLの場合)
もはや新規 Webサイトを UTF-8 以外で作成することはないと思いますが、この方法は、Shift_JIS で構築されている従来の Webサイトを扱う場合に場合に覚えておいて損はない方法です。仮に HTMLファイルの文字コードが Shift_JIS だったとしても、&#xNNNN; という文字参照形式で記載すれば Unicode だけにある新しい文字を文字化け無く表示することが可能です。 (ただし、閲覧側のブラウザやOSがある程度新しいものであることが条件です。)
文字参照は以下のように HTML::Entities モジュールを使って出力します。これは漢字だけでなく、Shift_JIS の日本語の中にフランス語の「ç」や、ドイツ語の「ß」、スペイン語の「ñ」を混ぜて表示したいというような場合にも使えます。 (ただし、日本語フォントのままだと文字化けします。CSSでのフォント指定もお忘れなく!)
use strict; use warnings; use utf8; use HTML::Entities; my $template = '...<p>__PLACE__</p>...<p>__PLACE2__</p>'; #Shift_JIS のHTMLテンプレート $template =~ s/__PLACE__/encode_entities('〠123-456')/ge; $template =~ s/__PLACE2__/encode_entities('フランス語の「ç」や、ドイツ語の「ß」、スペイン語の「ñ」')/ge; binmode STDOUT, ':encoding(cp932)'; print $template;さて、今回はまとまったプログラムではなく、色々な場合に変えていかなければならないコードサンプルの紹介になりました。文字化けは、これ一本でOK、といったすっきりした解決のない、なかなか奥深い問題なのです。
プログラマー的には頭が痛いわけですが、日本語が多くの文字と、それにこだわる豊かな文化を持っていることの証でもあるのです。大切にしたいですね。