dポイントプレゼントキャンペーン実施中!

文字コードで行き詰まりました。
同じUTF8で全く同じ文字列であるにも関わらず(外見上は全く同じにも関わらず)、2つの生の文字コードが異なるということが起きて、どうしたらいいのかわかりません。


WEBサイトから「あいう」という文字を取り出してきて、そいつの文字コードを、生で表示させました。
すると、
E38182E38184E38186
になります。ところが、perl文の中で$testStr="あいう";
として、そいつを同じように16進数で表示させると、
304230443046
と、違うものになっています。

にも関わらず、「WEBからとってきた文字列」も「perl内で書いた文字列」も両方「Jcode::getcode()」でその文字コードを調べると、utf8になります。
しかも、それら両方とも、print()をすると、同じように「あいう」と表示されます。

「perl内で書いた文字列」の方では、問題なくDBに入れれるのですが、「WEBからとってきた文字列」の方は、文字化けします。
「WEBからとってきた文字列」を「perl内で書いた文字列」のような文字コードに変換するにはどうすればよいのでしょうか?
なぜ同じUTF8として認識されているのでしょうか?


もう気が狂いそうです。
どなたかお助けください。お願いします。


環境
Ubuntu
perl v5.8.8 built for x86_64-linux-gnu-thread-multi


以下は詳しいソースです。
use utf8;#この質問に関係がありそうなモジュールです
use Jcode;
use DBI;
use URI;
use YAML;
use Switch;
use Web::Scraper;
use HTTP::Request::Common;
use LWP::UserAgent;
use HTTP::Cookies;
use HTTP::Request::Common qw(POST);

##Webサイトから文字列「あいう」を取得してくる。取得過程は略してます。
$testStrFromWeb=@titleArr[$i];#ちなみにWebサイトの文字コードはUTF8です。
print("testStrFromWeb:".$testStrFromWeb);#「あいう」と表示されます。
##Webサイトから得た文字列「あいう」の生の文字コードを表示。
$strCode=$testStrFromWeb;
$strCode =~ s/(.)/sprintf "%X", ord($1)/eg;
print "$strCode\n";#E38182E38184E38186と表示されます
##Webサイトから文字列「あいう」の文字コードの種類を表示(utf8が返ってくる)
my $getcode = Jcode::getcode(\$testStrFromWeb);
print $getcode ,"\n";#utf8と表示される


##perl内で書いた文字列。
$testStrInPerl="あいう";
print("testStrInPerl:".$testStrInPerl);#「あいう」と表示されます。
##perl内で書いた文字列「あいう」の生の文字コードを表示。
$strCode=$testStrInPerl;
$strCode =~ s/(.)/sprintf "%X", ord($1)/eg;
print "$strCode\n";#304230443046と表示されます
##perl内で書いた文字列「あいう」の文字コードの種類を表示
my $getcode = Jcode::getcode(\$testStrInPerl);
print $getcode ,"\n";#utf8と表示される

ここまで読んでくださってありがとうございます。

A 回答 (1件)

2つの文字列の違いはutf8フラグがついているかいないかです。



utf8フラグはperlが文字列として解釈できる内部コードに付くフラグなので、
内部コードであるか、そうでないかの違いと言うこともできます。

内部コード(utf8フラグがついた文字列)にはutf8が使われていますが、
utf8フラグの有無で挙動が変わるので、
実際にはutf8フラグがついた内部コードと、utf8フラグがついていないutf8バイト列は
別物と考えた方が分かりやすいです。


別物なので、外部から取ってきたデータがutf8のバイト列なら、
ちゃんとperlの内部コードに変換しなければなりません。
具体的には、
 my $decodedStrFromWeb = Encode::decode("utf8", $testStrFromWeb);
として、utf8を内部コードにデコードします。
デコードすることでperlはそれを文字列として解釈できるようになります。

逆に内部コードを外部(ファイルなど)に出力するときには、
内部コードからutf8などにエンコードすることが望ましいです。
PerlIOレイヤを使えば、このようなエンコード・デコード処理はある程度透過的に行えます。


なお、内部コードが出現するべき文脈で
内部コード以外のバイト列(utf8フラグが付いていない文字列のこと)が出現した場合、
perlはそのバイト列を内部コードに自動的に変換します。
しかし、この自動変換ではバイト列をlatin-1文字コードによる文字列と見なしてdecodeするため
たいていの場合予想外の変換結果になり文字化けしたように見えます。

例えば、
$utf8_flag . $no_utf8_flag;
の様な文字列連結を行う場合、utf8フラグが付いていない文字列が自動変換されるので、
$utf8_flag . Encode::decode("latin-1", $no_utf8_flag);
と書いたのと同じ事になります。


###Sample###
use utf8;
use strict;
use Jcode;
use Encode;

# ※標準出力にUTF8で出力する(PerlIOレイヤ)
#binmode STDOUT, ":utf8";

# utf8フラグが付いていないバイト列
my $testStrFromWeb=Encode::encode("utf8","あいう");

#↓「あいう」と表示される。ただし※のコメントを外すと文字化けする。
print "testStrFromWeb:".$testStrFromWeb."\n";
#↓文字化けする。
print "testStrFromWebを表示:".$testStrFromWeb."\n";

my $strCode=$testStrFromWeb;
$strCode =~ s/(.)/sprintf "%X", ord($1)/eg;
#↓E38182E38184E38186と表示される
print "$strCode\n";

my $getcode = Jcode::getcode(\$testStrFromWeb);
#↓utf8と表示される
print $getcode ,"\n";

# UTF-8を内部形式にデコードすれば、$testStrInPerlと同様に扱える。
my $decodedStrFromWeb = Encode::decode("utf8", $testStrFromWeb);


# utf8フラグが付いている文字列(=perlの内部形式)
my $testStrInPerl="あいう";
#↓「あいう」と表示される。
print "testStrInPerl:".$testStrInPerl."\n";

my $strCode=$testStrInPerl;
$strCode =~ s/(.)/sprintf "%X", ord($1)/eg;
#↓304230443046と表示される
print "$strCode\n";

my $getcode = Jcode::getcode(\$testStrInPerl);
#↓utf8と表示される
print $getcode ,"\n";

###End###

[参考]
Perl 5.8.x Unicode関連
http://www.rwds.net/kuroita/program/Perl_unicode …

UTF8 フラグあれこれ - daily dayflower
http://d.hatena.ne.jp/dayflower/20080219/1203493 …
    • good
    • 0
この回答へのお礼

Wernerさん、どうもこんなに早く、そして丁寧に教えてくださってありがとうございます。
誰も回答してくれなかったら本当に狂いそうでしたが、おかげさまでうまくいくことができました。そして文字コードトラブルの根本的理解にもなりました。

結局、
my $decodedStrFromWeb = Encode::decode("utf8", $testStrFromWeb);
がこの問題を直接解決する文になりました。

ここまで親切に解説していただくと、なぜかやる気までUpしてきましたw
本当にありがとうございました。

お礼日時:2009/07/11 16:57

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!