WindowsのUTF-16出力で文字化けが発生する

2007/04/18 (水)  カテゴリー/Perl

 多くの方がWindows上でPerlを使うさいにActivePerlを使用していると思います。私もその一人ですがちょっと前大きな問題に直面しました。

「UTF-16出力をすると文字化けが発生する!」

 そうなんです。過去の記事で日本語の扱いについて紹介しました。じつは、あれらの設定でMac OS X 等で処理しているぶんには問題ないのですがWindows上でActivePerlを使って出力すると特定の文字が化けてしまいます。
 特定の文字というのは「上」等の文字コードの中に「0A」なんていうバイナリーデータを含んでいる文字です。

use utf8;
binmode STDOUT, ":encoding(cp932)";
binmode STDERR, ":encoding(cp932)";
binmode STDIN, ":encoding(cp932)"; 

#-書込先ファイルの文字コードを指定
open(OUT, ">:encoding(UTF-16LE)", "write.txt");

print OUT "\x{FEFF}"; #-BOMを出力
print OUT "上\n";
close(OUT);
exit;


 上記のサンプルコードを実行し出力された「write.txt」の中身を16進ダンプすると、

「FFFE0D0A4E0D0A00」

 となっており化け化けです。まあこんなページをご覧の方ならお察しの通り改行コードがらみの処理がどーもうまくいっていないようです。
 この現象に初めて出会った時は原因を調べる時間もなく「utf8」で出力して「nkf」でUTF16にするなんてことをしていました。

 そして月日は流れ・・・リベンジです(>Д<)ゝいぇい!
 少々時間ができたので文字化けの原因を調べてみました。そしてついに発見Σ( ̄□ ̄;)!!

Writing a Unicode file via perl

 どうやらここの内容を見るに、

「デフォルトで働いてしまう改行変換を「:raw」を指定することで抑止しUTF-16にしてから改行を自分できちんと変換しましょう」

 ということのようだ。ということでサンプルスクリプトの書き込みファイルオープンの部分を以下のようにしてみた。

open(OUT, ">:raw:encoding(UTF-16LE):crlf:utf8", "write.txt");


 おぉー、ばっちり。これでWindowsのPerlからUTF-16LEの書き出しが出来るじゃないか。さっそく機会があったらためしてみようっと。

テーマ : プログラミング - ジャンル : コンピュータ

Perl 正規表現中の変数展開&「e」オプションの使い方

2007/04/12 (木)  カテゴリー/Perl

 Perlにおいて正規表現による文字列の置換をけっこう頻繁に使用しているのですが正規表現の指定に変数を交える場合に関してのちょっとしたまとめ。

【サンプルコード】※サンプルコード中の文字コードの指定などは環境に応じて適宜編集してください以下同様
#!/usr/bin/perl
use utf8;
binmode STDOUT, ":encoding(utf8)";
binmode STDERR, ":encoding(utf8)";
binmode STDIN, ":encoding(utf8)";

#変換元のテキスト(改行交じり)
$a = "あいうえお\n".
     "かきくけこ\n".
     "さしすせそ";

#正規表現パターン
$b = "あいう";

#置換処理
$a =~ s/(${b}えお)/($1)/m;

#結果の表示
print $a . "\n";

 処理結果↓
(あいうえお)
かきくけこ
さしすせそ

 ここで疑問になるのが正規表現で文末を表す位置指定子である「$」との関係だ。ためしに置換処理の行をこうしてみた。
$a =~ s/(${b}え)$/($1)/m;

 処理結果↓
あいうえお
かきくけこ
さしすせそ

 置換されなくなった、ということは位置指定子としてちゃんと機能しているということだ。さらにこうしてみるとどうだろう?
$a =~ s/(${b}え)$か/($1)/m;

 処理結果↓
(あいうえ)お
かきくけこ
さしすせそ

「$か」が変数展開され空文字列になってしまうので、パターンにマッチしてしまう。さらにこんな指定を間違ってするという場合、期待する動作は、
「次行の先頭が“か”だった場合の行末文字を・・・」
 なんて具合の処理かもしれない。次行の先頭にある文字を指定するのに「$か」では間違いである。そのような処理を施す場合には地道にロジックを考えるか位置指定子などを使用せずに「\nか」等の指定で処理するのがベターだと思う。

 さてここまで正規表現の中での変数展開の話をしてきたが当然置換文字列の中でも変数が利用できる。

 最初に実行したソースに置換文字列用の変数「$c」を加え、さらに置換処理を以下のようにしてみた。
#置換文字列
$c = "変数展開";

#置換処理
$a =~ s/(${b}えお)$/($c)/m;

 処理結果↓
(変数展開)
かきくけこ
さしすせそ

 置換文字列中の変数がちゃんと展開されていることがわかる。

 以上の結果から正規表現文字列の中でも、置換文字列の中でも変数展開が行われることが確認できた。まあなんということはない。変数展開後の文字列で置換処理を行っていると思えば簡単だ。さてせっかく変数展開の話題に触れたのでついでに正規表現の「e」オプションについても軽く書いておこうと思う。

 正規表現の「e」オプションはこれぞPerlの真骨頂といいたくなるほど強力だ。このオプションは置換後の文字列をPerlの実行文として評価するというものだ。Perlの命令でいう「eval」に相当する。

【サンプルコード】
#!/usr/bin/perl
use utf8;
binmode STDOUT, ":encoding(utf8)";
binmode STDERR, ":encoding(utf8)";
binmode STDIN, ":encoding(utf8)";

#欧文文字列の先頭1文字を大文字にするサブルーチン
sub Top2Big {
        my @data = split(//,$_[0]);
        my $Value;
        $data[0] =~ tr/[a-z]/[A-Z]/;
        $Value = join(//,@data);
        return $Value;
}

#変換元のテキスト
$a = "hello i am hirakun";

#置換処理
$a =~ s/(\w+)/&Top2Big($1)/eg;

#結果の表示
print $a . "\n";

exit;

 処理結果↓
Hello I Am Hirakun

 どうだろう。アルファベットを全て大文字にするのであれば「tr/[a-z]/[A-Z]/」を使用すれば簡単に変換することができる。しかし、
「英単語の先頭の1文字だけを大文字にしたい」
 となると、とたんに敷居が高くなる。他のスクリプト言語であれば変換文字列の中に実行文を埋め込んであとでまとめて変数展開をかけるなどの方法をとる必要があるかもしれないが、Perlの「e」オプションを使えばマッチした文字列を引数にしてサブルーチンに飛ばし、処理結果の文字列で置換するなんてことが出来てしまう。この記述方法がいかに有用か、他の言語で同様の処理を記述してみればその強力さに気がつくと思う。
 さらにこの「e」オプションにはおもしろい特徴がある。「e」という文字を指定した数だけ評価を行うというものだ。先ほどのサンプルコードのうち「変換元テキスト」「置換処理」「結果の表示」を以下のように変更し、実行してみた。
#変換元のテキスト
$a = 'hello i am hirakun';
$b = 'hello i am hirakun';
$Syori = '&Top2Big';

#置換処理
$a =~ s/(\w+)/$Syori.'('.$1.')'/eg;
$b =~ s/(\w+)/$Syori.'('.$1.')'/eeg;

#結果の表示
print $a . "\n";
print $b . "\n";

 処理結果↓
&Top2Big(hello) &Top2Big(i) &Top2Big(am) &Top2Big(hirakun)
Hello I Am Hirakun

 結果の一行目が「e」オプションを一文字で指定したもの、二行目が「e」オプションを二文字で指定したものだ。  一度だけ指定した場合、置換文字列を実行文として評価し「変数展開と文字列の連結」が行われているのがわかる。二文字指定したものでは「変数展開と文字列の連結」が行われ、さらにその結果の文字列に対して再度評価した結果が反映されているのが見て取れる。このように「e」オプションは複数することで実行後の実行後の実行後の・・・と文字列を処理することが可能だ。  最後に一つ注意しなければいけないのだが「e」オプションは単純に変数展開をするのではなくあくまでも「実行文として評価する」オプションである。サンプルコードでも書いたように「e」オプションをつけた際には置換後の文字に()などを付加したい場合はきちんと文字列の連結子であるピリオド等を使わなければならない。さらに半角()等実行結果として表示されない文字は当然置換後の文字に含まれなくなるのでそれらの文字が含まれる文字列を操作する場合はそれなりの配慮が必要となる。

テーマ : プログラミング - ジャンル : コンピュータ

Perlで変数名と文字列を明示的に分ける方法

2007/02/15 (木)  カテゴリー/Perl

 先日会社の人に、

Perlで文字列と変数名を明示的に分けて表記するにはどうするの?」

 みたいなことを聞かれたので軽くメモ。知ってるようで知らないってのが、いろんなジャンルにはあるけどもこの話しもその類の話みたい。
 Perlなんかの参考書では最初の方のページにちょろっと書かれているだけだったりし一度見落とすと再度みる可能性が低いというのが主な理由なんでしょうな。

 例えばこんな時、

$Atai_a = 'Hira';
$Atai_b = 'kun';
print "$Atai_a$Atai_b\n";

 こうすると結果は、

「Hirakun」

 になる。さてでは「kun」の部分を変数に入れるのではなく直接print文に記入するとどうだろう。

$Atai_a = 'Hira';
print "$Atai_akun\n";

 まあ至極簡単なので想像できますがprint文の中で使用されている変数名が「$Atai_akun」になってしまうので「Hirakun」なんていう結果はでてきません。
 このように$Atai_akunとプログラム上に書いてしますと当然変数と文字列の堺が無くなってしまいます。

「$Atai_aという変数とkunという文字を並べているんだよ」

 ということを明確に表すにはどうすればいいのでしょうか。それには「{}」を使います。次がその例です。

$Atai_a = 'Hira';
print "${Atai_a}kun\n";

 このように変数名の部分を「{}」で囲むことにより明示的に変数名の範囲を指定することができます。この書き方はPerlに限らず他の言語でも似たりよったりなので、

「変数名を明示的に示したい時はどうすればいいのか?」

 ということは最初に押さえておきたいポイントの一つですな。まあこれ、知ってる人にとっては何を偉そうに長々と、と思える内容なのですが知らないと本当に悩むんですよね。まあこんな「{}」を使わずとも文字連結子である「.」ピリオドを使えば解決できる問題ですが。

$Atai_a = 'Hira';
print $Atai_a . 'kun' . "\n";

 好みの問題ですかねえ。私の場合は根が怠け者なのでダブルクォートで全部囲ってしまうのがくせになってます。ですので「{}」はかなり重要なんですよね。そうそう文字連結の話題で思い出したのですがある変数に文字列を追加したい時ってありますよね? Perlでは数字のカウントなどで、

$a++;

ような書き方がありますが、これ文字にも似たようなものがありまして、

$Name = 'ひら';
$Name = $Name . 'くん';

 のような物を、

$Name = 'ひら';
$Name .= 'くん';

なんて感じで「.=」を使って記述できます。この記述を「わかりやすい」と感じるか「わかりにくい」と感じるかは人それぞれですが、最近覚えたのでついつい使ってしまんですよ。本当は誰がみてもわかるような表現で書くべきなんだろうなあ。それが一番の悩みどころですかね
( ̄▽ ̄;

テーマ : 初心者のために・・・ - ジャンル : コンピュータ

タグ : Perl

Perl 5.8.x日本語まとめについて

2007/02/06 (火)  カテゴリー/Perl

 結局技術的な話しに終始してしまう当ブログ(^^; なんだかんだでコンピュータやインターネットのことに一番興味があるんだなあ自分。備忘録もかねて日記を何て思っていましたが断然備忘録の割合が多いですな。


 Perl 5.8の日本語周りは本当にわかりにくく全貌をつかむのに苦労します。いままでJperlを使用してきた人が使うとおそらくいきなり躓く可能性が高いです。前回のエントリーにある日本語処理の簡単なまとめはそれらの躓き安い問題の解説や注意点を一切合切抜きにして、
Perl 5.8.xで日本語をとりあえず使うにはどうすればいいのか」
 ということをまとめたものです。なぜあの設定にするとうまくPerlの内部で日本語が処理されるかということに関してはGoogleなり各所詳細な解説頁がありますのでそちらを参考にしてください。以下に前回のまとめ記事を作成する際に参考にさせていただいた頁を列記いたします。

Perl 5.8.x Unicode関連
Perl-5.8 覚え書き
Perl 5.8.x における日本語コード変換のメモ

 有益な情報を提供してくださる方々にこの場をかりて感謝いたします。また自分もこのような有益な情報を提供できたらなあと日々思ってみたりしてます( ̄▽ ̄; もっともっと充実させよっと。

テーマ : プログラミング - ジャンル : コンピュータ

タグ : Perl

Perl 5.8での日本語文字コードの扱いについて

2007/02/05 (月)  カテゴリー/Perl

Perl 5.8で日本語を利用するさいに、もっとも簡単で単純だと思われる方法を以下にまとめます。

PerlのソースファイルはUTF8で記述し、以下をソースの先頭で宣言します。
use utf8; #-ソースがUTF8だという宣言
binmode STDOUT, ":encoding(cp932)"; #-画面に出力したい文字コード
binmode STDERR, ":encoding(cp932)"; #-エラー出力に使いたい文字コード
binmode STDIN, ":encoding(cp932)"; #-標準入力から入ってくる文字コード


 上記の「:encoding(文字コード)」の文字コード部分に指定できるコードの一部は以下のとおりです。

euc-jp
shiftjis
cp932
MacJapanese
utf8
UTF-16
UTF-16BE
UTF-16LE
UTF-32
UTF-32BE
UTF-32LE


■ファイルハンドルをオープンする場合にはエンコードを必ず指定します。

open(IN ,"<:encoding(shiftjis)", "read.txt"); #-読込元ファイルの文字コードを指定
open(OUT, ">:encoding(shiftjis)", "write.txt"); #-書込先ファイルの文字コードを指定


 ただしUTF8のファイルを読書するときは、

open(IN,"<:utf8","read.txt);

 ↑のように「encoding」の部分をはずします。理由は「use utf8」を先頭で宣言しているからです。「use utf8」を宣言するとPerlの内部は基本的に全てUTF8の文字列として処理されます。ファイルの文字コードがUTF8の場合はエンコードの指定をすると多重エンコードになってしまいます。
※use utf8をしていない場合はソースの文字コードと文字コードが異なるハンドルへの全角文字出力にデコード処理が必要です。デコード処理を行いたい場合は先頭の宣言に以下のような2行を追加し、デコードを行いたい箇所でdecode関数を使います

use Encode;
use Encode 'decode', 'encode';



print OUT decode('utf8',"デコード処理n");


 以上がごくごく簡単なPerl 5.8での日本語の取り扱いです。誤字脱字、間違った説明やもっといい方法を知っている等々、なにかございましたら「macjperlあっとgmail.com」までご連絡ください<(_ _)>

テーマ : プログラミング - ジャンル : コンピュータ

タグ : Perl

あわせて読みたい