プログラムからCSVファイルやHTMLファイルなどの外部ファイルを読み込む場合、正しく文字コードを指定しないと文字化けします。一方、毎回外部ファイルに合わせてプログラム内の指定を変えているとプログラムの汎用性が落ちてしまいます。Perl モジュール Encode::Guess を使って文字コードを自動的に判別して開く方法を紹介します。

文字コードと文字化け

まずは文字コードについて簡単におさらいしましょう。

CSVやHTMLなどのファイルを保存する場合は常に「文字コード」を指定する必要があります。意識的に文字コードを指定していない場合も、使用しているアプリケーションが自動的に選択して保存しています。日本語文字を保存できる文字コード (正しくは「エンコーディングスキーム」または「文字符号化方式」) の主なものは以下の4つです。
  • UTF-8
  • Shift_JIS ※1
  • EUC-JP
  • ISO-2022-JP
※1 OS独自の文字(いわゆる外字)を加えたものなど亜種が多数あります。

一方これらのファイルを Perl プログラムから開く場合、ファイルがどの文字コードを利用しているかが予め分かっていないとファイルを正しく開く事ができません。間違った文字コードを指定すると、いわゆる「文字化け」の状態になります。

例えばこれまでファイルを開く時に使っていた以下のコードでは、「cp932」が文字コードの指定です。
open ($fh, '<:encoding(cp932)', FILENAME.csv)
「cp932」は Shift_JIS の亜種で 日本語 Windows OS で標準で採用されている文字コード(※2)に対応する Perl での文字コード指定です。例えば MS−エクセルで CSV ファイルを作成するとこの文字コードになります。Macで保存したときもこれに似た Shift_JIS の亜種になります(MacJapanese)。2大OSで Shift_JIS 系の文字コードが採用されているから「cp932」決め打ちでよいかと思うとそうでもなく(※3)、国際化 (多数の言語の文字表示) に対応している UTF-8 が採用される事も多くなっています。

※2 Shift_JIS 外字を加えたもので、Windows OS の用語で「CP932」と呼ばれています。
※3 外字を含まない Shift_JIS に対応する Perl の文字コード指定「shiftjis」もありますが、外字が全て文字化けするので通常は使いません。

という事で、ファイルを開くときは必ず対象ファイルの文字コードを予め把握して指定する事が必要ですが、毎回外部ファイルに合わせてプログラム内の指定を変えるのは面倒ですし、開くファイルの文字コードが分からない、または、文字コードが混在してしまっている場合もあります。

このような場合に、文字コードを自動的に判別して開く方法を紹介します。Perl モジュール Encode::Guess を使います。

Encode::Guess モジュールの使い方

Encode::Guess は Perl の標準モジュールです。基本的な使い方は以下の通りです。太字の部分がこのモジュールを使った処理の部分です。

use strict;
use warnings;
use utf8;
use Encode::Guess qw/cp932 euc-jp 7bit-jis utf8/; #文字コードの候補を指定して use する

my $file = shift;
my $fh;

unless (open ($fh, '<:raw', $file)){ #文字コード指定なしで1回ファイルを開く
	die "OPEN FAILED: $file, $!
";
}
my ($temp, $i);
while ($temp .= <$fh> and ++$i < 50){ #最大50行(実は49行)まで変数に保存
	eof and last;
}
close ($fh);
my $genc = guess_encoding($temp);

printf 'Encoding of file %s is %s', $file, $genc->name;
print "
";
文字コード判別のためには、ファイルの一部分だけを取り出し、その部分に含まれる文字から文字コードを判別します。上のコードではファイルの先頭から50行を判別材料として取り出していますが、もし扱うファイルの先頭に記号などの日本語文字でない文字が連続して現れる事が分かっている場合は、その部分をスキップして取り出すか、もう少し多めに取り出して下さい。

このコードを guess.pl の名前で保存すると以下のコマンドでファイルの文字コードが表示できます。色々なファイルで試して見て下さい。
perl guess.pl (ファイル名)

次は:> Encode::Guess を使うときの注意点