文字単位に分割する
Perl/CGIプログラム を使って、指定した文字列から1文字ずつ取り出して表示させてみましょう。
ShiftJisコード、EUCコードとUTF-8コードの文字列をそれぞれ文字単位に分割します。
文字数をカウントする方法 で紹介したEncodeモジュールのdecode関数を使うと全角文字であっても普通に length関数 が使えることがわかりましたよね。
なので今回も同じようにEncodeモジュールのdecode関数を使って、全角文字であっても半角文字と同じように扱えるような環境にしてからパターンマッチを使って文字列を文字単位に分割します。
ShiftJisコードの場合
ShiftJisコードの文字列を文字単位に分割して表示させてみましょう。
#!/usr/bin/perl
use strict;
use Encode;
my $string = 'Perl/CGI入門';
my $decoded = decode('sjis', $string);
my @character = $decoded =~ /./g;
foreach (@character) {
$_ = encode('sjis', $_);
}
print "Content-type: text/html; charset=shift_jis\n\n";
print join ("\x20", @character);
exit;
Perl/CGIプログラムサンプルを実行すると「P e r l / C G I 入 門」と表示されます。
ShiftJisコードの文字列「Perl/CGI入門」を文字単位に分割し表示させる際、半角スペースの 16進数 「\x20」をはさんでいます。
Perl/CGIプログラム解説
文字列中には半角文字と全角文字が混在しています。
ShiftJisコードの場合、半角文字は1バイト、全角文字は2バイトです。
といっても、 Perlパッケージ 側から見れば、何の意味もないただのバイト列に過ぎません。
なのでlength関数などはとりあえず1バイトずつ処理するわけです。
それではこまるので、まずはこの何の意味も持たないただのバイト列に対して、文字列であることを定義づけます。
my $decoded = decode('sjis', $string);
イメージ的には…
変数「$string」内が「字字字字字」なのに対し。
変数「$decoded」内は「字|字|字字|字」という感じでしょうか。
「字」は1バイト文字、「字字」は2バイト文字のつもりです。
ただ羅列してあるだけではわからないですよね。
なので文字と文字の区切りに「|」を入れることにより、1文字1文字を把握できるようになるわけです。
これで、1バイトの半角文字も、2バイトの全角文字も同じ「1文字」になりました。
次に パターンマッチ を使って文字列を文字単位に分割し、 配列 に格納します。
my @character = $decoded =~ /./g;
配列「@character」の各要素に1文字ずつ格納されているので、要素数=文字数となります。
なので例えば、「my $count = scalar @character;」とすれば文字数を求めることができます。
文字数をカウントする場合であればここまででよかったのですが、今回は文字単位に分割された1文字1文字を表示させなくてはいけないので、もう一工夫します。
foreach (@character) {
$_ = encode('sjis', $_);
}
これは何をしているのかというと…。
Perlパッケージから見た文字を、われわれから見た文字に戻しています。
配列「@character」の各要素には、バイト数に関係なく1文字入っています。
性格には、「1文字」と前述の「これが1文字であるという宣言」です。
前述のイメージだと「字|」とか「字字|」です。
もしこのまま表示させてしまうと「|」を、ウェブブラウザが誤って解釈する可能性があるので、この「|」を取り除く必要があります。
この作業をしているのが foreachループ 部分の処理です。
Encodeモジュールのencode関数を使って、配列「@character」の各要素に対してひとつずつ処理しています。
ちなみに、見てわかるように特殊変数「$_」を変更すると、配列「@character」の各要素にも変更が反映されます。
これで配列「@character」内の全ての文字が表示できるようになりました。
print "Content-type: text/html; charset=shift_jis\n\n";
print join ("\x20", @character);
配列「@character」を「print @character;」としてしまうと違いがわからなくなるので、 join関数 で半角スペースをはさみつつ表示させています。
EUCコードの場合
EUCコードの文字列を文字単位に分割して表示させてみましょう。
#!/usr/bin/perl
use strict;
use Encode;
my $string = 'Perl/CGI入門';
my $decoded = decode('euc-jp', $string);
print "Content-type: text/html; charset=euc-jp\n\n";
while ($decoded =~ /(.)/g) {
print encode('euc-jp', $1);
print "\x20";
}
exit;
Perl/CGIプログラムサンプルを実行すると「P e r l / C G I 入 門 」と表示されます。
Perl/CGIプログラム解説
コーディングは違いますが、やってることは前述のShiftJisコードのものと同じです。
まず、Encodeモジュールのdecode関数を使って、文字単位を定義します。
my $decoded = decode('euc-jp', $string);
次に パターンマッチ を使って1文字ずつマッチさせ、取り出した文字をEncodeモジュールのencode関数で純粋な文字データにしてから表示させています。
print "Content-type: text/html; charset=euc-jp\n\n";
while ($decoded =~ /(.)/g) {
print encode('euc-jp', $1);
print "\x20";
}
文字数分だけ whileループ が行われます。
なので厳密には、一番最後は半角スペースが出力されます。
UTF-8コードの場合
UTF-8コードの文字列を文字単位に分割して表示させてみましょう。
#!/usr/bin/perl
use strict;
use Encode;
my $string = 'Perl/CGI入門';
$_ = decode('utf-8', $string);
s/(.)/"\x20".encode('utf-8',$1)/eg;
print "Content-type: text/html; charset=utf-8\n\n";
print;
exit;
Perl/CGIプログラムサンプルを実行すると「 P e r l / C G I 入 門」と表示されます。
Perl/CGIプログラム解説
これもコーディングは違いますが、やってることは前述のShiftJisコードやEUCコードのものと同じです。
まず、Encodeモジュールのdecode関数を使って、文字単位を定義します。
$_ = decode('utf-8', $string);
次に パターンマッチ を使って1文字ずつマッチさせ、特殊変数「$1」に格納します。
こうして取り出した1文字を、Encodeモジュールのencode関数で純粋な文字データにしつつ頭に半角スペースを付加します。
s/(.)/"\x20".encode('utf-8',$1)/eg;
このコードの意味がよくわからない場合は、 Perl/CGIプログラムの置換演算子 を参照してください。
あとは特殊変数「$_」を表示させて終了です。
print "Content-type: text/html; charset=utf-8\n\n";
print;
これも厳密には、最初の文字が半角スペースになります。