Perl

3種類の文字化け原因とPerlでの対応方法(2ページ目)

Perlプログラマーは文字化けを回避するために何ができるでしょうか? 一部の文字化けは不可避的なものです。この記事では、文字化けを3種類に分類しその原因をおさらいします。また、プログラミングで解消できる文字化け対策をご紹介します。

井上 みやび子

執筆者:井上 みやび子

Perlガイド

文字化け対応の指針

さて、日本語の文字化けにはざっと上記の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、といったすっきりした解決のない、なかなか奥深い問題なのです。

プログラマー的には頭が痛いわけですが、日本語が多くの文字と、それにこだわる豊かな文化を持っていることの証でもあるのです。大切にしたいですね。
【編集部おすすめの購入サイト】
楽天市場で Perl 関連の書籍を見るAmazon で Perl 関連の書籍を見る
  • 前のページへ
  • 1
  • 2
※記事内容は執筆時点のものです。最新の内容をご確認ください。
※OSやアプリ、ソフトのバージョンによっては画面表示、操作方法が異なる可能性があります。

あわせて読みたい

あなたにオススメ

    表示について

    カテゴリー一覧

    All Aboutサービス・メディア

    All About公式SNS
    日々の生活や仕事を楽しむための情報を毎日お届けします。
    公式SNS一覧
    © All About, Inc. All rights reserved. 掲載の記事・写真・イラストなど、すべてのコンテンツの無断複写・転載・公衆送信等を禁じます