連投申し訳ありません。
「まるごとPerl」(2006年9月、インプレス刊)という本の「まるごとEncode」という記事に従ってEncodeの勉強をしています。
原記事は UNIX(というか端末コードをUTF-8に出来る環境)ですが、それをWindowsに移植しようとして苦労しています。
#! perl
# list3 -- UTF-8モードとバイトモードの切り替え
# インデントを表現するために全角空白を使っています
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print substr($text, 3, 4); # カタカナと表示されたい
__END__
というプログラムを実行すると
C:\>list3.pl
カタカナカタカナ
と表示されてほしいのですが、
C:\>list3.pl
Cannot decode string with wide characters at C:/strawberry/perl/lib/Encode.pm li
ne 174.
と表示されます。
use utf8 を後ろにズラして
#! perl
# list3 -- UTF-8モードとバイトモードの切り替え
# インデントを表現するために全角空白を使っています
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい
}
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8; # 移動
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print substr($text, 3, 4); # カタカナと表示されたい
__END__
とするとうまく動いて
C:\>list3.pl
カタカナカタカナ
と表示されます。
#! perl
# list3 -- UTF-8モードとバイトモードの切り替え
# インデントを表現するために全角空白を使っています
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
my $text = '漢字、カタカナ、ひらがなの混じったtext'; # 移動
print substr($text, 3, 4); # カタカナと表示されたい # 移動
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい
}
__END__
のようにするとやはり最初と同じエラーになりますが、
#! perl
# list3 -- UTF-8モードとバイトモードの切り替え
# インデントを表現するために全角空白を使っています
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
my $text = '漢字、カタカナ、ひらがなの混じったtext'; # 移動
print substr($text, 3, 4); # カタカナと表示されたい # 移動
no utf8; # 追加
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい
}
__END__
だと大丈夫です。
結果として、ブロックの外で use utf8;、中では use bytes; という記事の著者の意図通りに動作しないようで、ブロックの中まで use utf8; が効いているようです・・・。
これは Perl の実装が変わったのでしょうか。
使用しているのは Windows XP Home SP3+Strawberry Perl v5.10.0 です。
No.1ベストアンサー
- 回答日時:
「まるごとPerl」という書籍を所有していないので、そこでどのようなコードが
書かれているのかが分かりませんが、以下のように一旦、変数に格納するとうま
く動作するようです。
試した環境は、
OS:Windows XP SP2
Perl:ActivePerl 5.8.9
です。
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
#-- まず一時変数に代入 --------------------------
my $clipped_text = substr($text, 9, 12);
#------------------------------------------------
print Encode::decode('UTF-8', $clipped_text); # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print substr($text, 3, 4); # カタカナと表示されたい
__END__
下記エラー内容から推測して、
>Cannot decode string with wide characters at
>C:/strawberry/perl/lib/Encode.pm line 174.
内部表現の文字をdecodeしようとして失敗しているのではと思い、一旦、encode
してみました。
use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 追加
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', Encode::encode('utf8', substr($text, 9, 12))); # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print substr($text, 3, 4); # カタカナと表示されたい
__END__
そうすると、エラーが出なくなったので、バイトストリームとしてではなく内部
表現のまま処理されているようです。
引数の中でsubstrを使った場合、use bytesが効いていないように見えます。
バグなのか仕様なのか、はたまた私の勘違いなのかはわかりません。
私より詳しい人が解明してくれるでしょう。
この回答への補足
#長すぎるようなので補足=>お礼の順番に書きます。これが前半です。
回答ありがとうございます!
おっしゃるように、私が改造した Windows 改造版ではなく、元の本に書いてあった(たぶんUNIX用の)プログラムをそのまま書き写します。(注釈は私です。)
#! perl
# list4org.pl -- UTF-8モードとバイトモードの切り替え(UTF-8版)
use strict;
use warnings;
# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;
{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print substr($text, 9, 12); # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
binmode STDOUT, ':utf8';
print substr($text, 3, 4); # カタカナと表示されたい
__END__
これを実行すると、なんと、エラーなく実行できます。
C:\>list4org.pl
翫き繧ソ繧ォ繝翫き繧ソ繧ォ繝
こんなことばっかりやっていると、これぐらいの UTF-8 は読めるようになってきますが(ウソ)これは正しく「カタカナカタカナ」と表示されています。
同じ本に書いてあったのですが、Devel::Peek::Dump というのを使うと、変数の UTF-8 フラグが覗けるので、見てみました。
#これが後半です。
#! perl
# list4org.pl -- UTF-8モードとバイトモードの切り替え(UTF-8版)
use strict;
use warnings;
use Devel::Peek; # <<<<< 追加
use utf8;
{
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
Dump $text; <<<< 追加
print substr($text, 9, 12); # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
Dump $text; <<<< 追加
binmode STDOUT, ':utf8';
print substr($text, 3, 4); # カタカナと表示されたい
すると、上の $text は両方ともUTF8 フラグは立っています。
C:\>list4org.pl
(略)
FLAGS = (PADMY,POK,pPOK,UTF8)
(略)
FLAGS = (PADMY,POK,pPOK,UTF8)
(略)
そこで、#1さんのおっしゃるように、中間変数に substr の結果を取ってやってみます。
#! perl
# list4org.pl -- UTF-8モードとバイトモードの切り替え(UTF-8版)
(略)
{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
Dump $text;
my $clipped_text = substr($text, 9, 12);
Dump $clipped_text;
print $clipped_text; # カタカナと表示されたい
}
my $text = '漢字、カタカナ、ひらがなの混じったtext';
Dump $text;
binmode STDOUT, ':utf8';
print substr($text, 3, 4); # カタカナと表示されたい
すると、$clipped_text からは UTF8 フラグが倒れています。
C:\>list4org.pl
(略)
FLAGS = (PADMY,POK,pPOK,UTF8)
(略)
FLAGS = (PADMY,POK,pPOK)
(略)
FLAGS = (PADMY,POK,pPOK,UTF8)
(略)
つまり、use bytes; は、my $text = '漢字、カタカナ、ひらがなの混じったtext';という変数定義および代入には効果を持たず、substr 関数および print 関数の挙動に影響を及ぼしているように見えます。(もし printにも影響を持たなければ、ブロックの中の print で Wide character と怒られるハズ)
おかげでグッと真相に迫ったような気がします。
ありがとうございます!
No.8
- 回答日時:
print (Encode::is_utf8(substr($text, 3, 3)) ? "substr is ".substr($text, 3, 3)." and is utf8\n" : "substr is ".substr($text, 3, 3)." and is no utf8\n");
これの動作ですが、まず、最初の substr($text, 3, 3) はis_utf8の引数となっ
ているので、文字ベースの処理が行われ、UTF8フラグの立った文字列が返り値と
なります。
したがって、Encode::is_utf8(substr($text, 3, 3) は真となります。
次に、"substr is ".substr($text, 3, 3)." and is utf8\n" が評価されますが、
この中で、substr($text, 3, 3)は引数の中で使われていないので、バイト列処理
が行われます。
なので、文字列切り出しの動作として、3オクテット目から3オクテットで「字」
を返すという動作は正しいです。
substr($text, 3, 3)が、引数の中で使われるように、ラッパー関数(return_str)
をかませた以下を実行すれば、意図した通りの結果が出ると思います。
use strict;
use warnings;
use utf8;
use bytes;
use Encode;
sub return_str { shift }
my $text = '漢字ひらがなカタカナEnglish';
print Encode::is_utf8( substr($text, 3, 3) ) ?
"substr is " . return_str( substr($text, 3, 3) ) . " and is utf8\n" :
"substr is " . return_str( substr($text, 3, 3) ) . " and is no utf8\n";
__END__
あーバカでした、すみません!!!
完全に解明しました。
面白いからperlbugしてみようかな???
みなさん、本当にありがとうございました!!!
No.7
- 回答日時:
>substr 関数が use bytes によってバイトベースで機能して(ゼロ始まりの)9
>バイト目から12バイトを取得するが、その文字列としては UTF8 フラグが依然
>として立っているので、さらに decode すると怒られる、ということなのでしょ
>うね。(ですから、encode してから decode すればうまくいった)
後半にちょっと誤解があると思います。
バイト列として処理した場合は、結果の文字列もUTF8フラグが落ちたバイト列とな
ります。
なので、普通なら質問者さんのコードはうまく機能するはずです。
ところが、意図したバイト列処理が行われずに、文字ベースの処理がなされており
(9文字目から12文字分を取得)、結果、UTF8フラグのついた文字列が返り値となっ
ています。
どうやら、substrを関数の引数として使うと、use bytesを無視して文字ベースとし
て処理されるみたいです。
No.6さんがその辺の説明をコードを交えながら詳細にされています。
すみません、やっぱり分かっていませんでした~?
下のコードを実行します。
#! perl
# expBytes.pl -- Enperiment of bytes
use strict;
use warnings;
use utf8;
use bytes;
use Encode;
my $text = '漢字ひらがなカタカナEnglish';
print (Encode::is_utf8($text) ? "text is $text and utf8\n" : "text is $text and no utf8\n");
my $subtext = substr($text, 3, 3);
print (Encode::is_utf8($subtext) ? "subtext is $subtext and utf8\n" : "subtext is $subtext and no utf8\n");
print (Encode::is_utf8(substr($text, 3, 3)) ? "substr is ".substr($text, 3, 3)." and is utf8\n" : "substr is ".substr($text, 3, 3)." and is no utf8\n");
print (Encode::is_utf8(bytes::substr($text, 3, 3)) ? "bytes::substr is ".bytes::substr($text, 3, 3)." and is utf8\n" : "bytes::substr is ".bytes::substr($text, 3, 3)." and is no utf8\n");
__END__
結果はこうなります。(Windows の場合、文字化けするが、リダイレクトしたもの)
text is 漢字ひらがなカタカナEnglish and utf8
subtext is 字 and no utf8
substr is 字 and is utf8
bytes::substr is 字 and is no utf8
この、
print (Encode::is_utf8(substr($text, 3, 3)) ? "substr is ".substr($text, 3, 3)." and is utf8\n" : "substr is ".substr($text, 3, 3)." and is no utf8\n");
の結果が、
substr is 字 and is utf8
なのがおかしい、というお話ですが、substr 関数が Encode::is_utf8 の引数となることによって、UTF8 フラグが立つのはおかしいんですが、文字列切り出しの動作としては3オクテット目から3オクテットで「字」を返している、ということでいいでしょうか?
(文字ベースで動作するのであれば戻り値は「ひらが」?)
たびたび申し訳ありませんが、ご無理のない範囲で返信いただけると幸甚です。
どうもありがとうございます!
No.6
- 回答日時:
既に気づかれているようですが、
use bytes; はutf8フラグには影響しません。
文字列リテラルにutf8フラグを付けたくない場合に使うのは
no utf8;です。
###---Sample---###
use strict;
use warnings;
use utf8;
use Encode;
{
use bytes;
my $text = "テスト";
print (Encode::is_utf8($text) ? "text: utf8\n" : "text: noutf8\n"); #->utf8
my $subtext = substr($text, 3, 3);
print (Encode::is_utf8($subtext) ? "subtext: utf8\n" : "subtext: noutf8\n"); #->noutf8
print (Encode::is_utf8(substr($text, 3, 3)) ? "substr: utf8\n" : "substr: noutf8\n"); #->utf8
print (Encode::is_utf8(bytes::substr($text, 3, 3)) ? "substr: utf8\n" : "substr: noutf8\n"); #->noutf8
}
{
no utf8;
my $text = "テスト";
print (Encode::is_utf8($text) ? "text: utf8\n" : "text: noutf8\n"); #->noutf8
my $subtext = substr($text, 3, 3);
print (Encode::is_utf8($subtext) ? "subtext: utf8\n" : "subtext: noutf8\n"); #->noutf8
print (Encode::is_utf8(substr($text, 3, 3)) ? "substr: utf8\n" : "substr: noutf8\n"); #->noutf8
}
###---End---###
use bytes;で何が変わるのかというと、
文字列操作関数が呼ばれるときに、文字列をバイト単位で扱う関数が呼ばれるようになります。
(しかし、既に指摘されているように、引数内で使ったときはなぜかbyte系の関数が呼ばれていないみたいですね。)
bytes - perldoc.perl.org
http://perldoc.perl.org/bytes.html
それから、実験する場合は別として、通常use bytes;は使わない方が良いです。
(例えば、以下の参考ページに使わないようにと書かれています。)
perlunifaq - Perl Unicode FAQ
http://perldoc.jp/docs/perl/5.10.0/perlunifaq.pod
2008-06-25 - daily dayflower
http://d.hatena.ne.jp/dayflower/20080625
文字列をオクテットストリームとして扱いたいならencodeしましょう。
###---Sample---###
use strict;
use warnings;
use utf8;
binmode STDOUT, ':encoding(cp932)';
my $text = '漢字、カタカナ、ひらがなの混じったtext';
my $encoded_text = Encode::encode('UTF-8', $text);
print Encode::decode('UTF-8', substr($encoded_text, 9, 12));
###---End---###
ありがとうございます。
納得しました。
二つの問題(カン違いと、Perlのヘンな挙動)を混乱してました。
リンクされた文書も読みました。
ありがとうございます!
No.5
- 回答日時:
use bytesは、use utf8の打ち消しではないので、補足の挙動は正しいと思います。
直に書いたリテラル文字は、use bytes下でもuse utf8のスコープ内であればUTF8
フラグは立つでしょう(No.2さんのコード)。
use bytesは、length, substr, index等の文字列を扱う関数で、UTF8フラグが立っ
ている文字列であってもバイト処理をするように指示するプラグマだと思います。
しかし、質問者さんが提示したコードだと何故かバイト列でなく文字ベースとして
解釈(character semantics)されているようです。
No.4さんの方法だとうまくいくみたいですね。
ありがとうございます!
「use bytesは、use utf8の打ち消しではなくlength, substr, index等の文字列を扱う関数で、UTF8フラグが立っている文字列であってもバイト処理をするように指示するプラグマ」ということですね。
最初に書いた、
{
# ブロックの外側ではバイトモードが強制される
use bytes;
my $text = '漢字、カタカナ、ひらがなの混じったtext';
print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい
}
では、substr 関数が use bytes によってバイトベースで機能して(ゼロ始まりの)9バイト目から12バイトを取得するが、その文字列としては UTF8 フラグが依然として立っているので、さらに decode すると怒られる、ということなのでしょうね。
(ですから、encode してから decode すればうまくいった)
ありがとうございます。
No.4
- 回答日時:
原因はわかりませんが、何が起きているか分かったような気がします。
> print Encode::decode('UTF-8', substr($text, 9, 12));
で、サブルーチンの引数として、substr で切り出した値を
渡していますが、これが use bytes で扱われていません。
回避方法としては、サブルーチンに渡す前に切り出す
my $sub_text = substr($text, 9, 12);
print Encode::decode('UTF-8', $sub_text);
明示的に bytes::substr を使う
print Encode::decode('UTF-8', bytes::substr($text, 9, 12));
という方法があります。
No.2
- 回答日時:
use utf8 の効果を消すのは no utf8 じゃないですか。
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw( Dumper );
use utf8;
my $str_utf8 = 'あいうえお';
my $str_noutf8;
my $str_bytes;
{
no utf8;
$str_noutf8 = 'あいうえお';
}
{
use bytes;
$str_bytes = 'あいうえお';
}
print Dumper( [ $str_utf8, #=> "\x{3042}\x{3044}\x{3046}\x{3048}\x{304a}",
$str_noutf8, #=> 'あいうえお',
$str_bytes ] #=> "\x{3042}\x{3044}\x{3046}\x{3048}\x{304a}"
);
exit;
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- CGI perlで書いたcgiでsqliteの使い方を教えてください 2 2023/05/08 21:29
- Excel(エクセル) エクセルのVBAについて とあるサイトのコードを参考に、CSVの文字化けを直すVBAを作成しているの 7 2022/11/04 14:15
- HTML・CSS HTML、cssのatomつぅーやつで 課題Ex1ってやつを表示させたいのですが、 私は課題Ex1が 2 2022/12/15 16:56
- Visual Basic(VBA) VBA 「,」・空白・カタカナ等の複数条件のマクロ 2 2023/08/23 11:57
- アプリ Android studio 初心者 Hello Android!しか表示されない 1 2023/08/01 08:47
- JavaScript 入力フォームの javascript で メールアドレスの正規チェックをを行い、ボタンをクリックして 2 2022/04/27 16:06
- JavaScript jqueryを使ったスムーススクロールのコードを書いたのですが、HTMLコード内にある、a butt 2 2022/04/14 10:59
- Perl Perl の外部モジュールの利用方法 3 2022/07/10 18:34
- PHP Content-Typeが機能していない? 2 2022/07/17 11:10
- JavaScript jQueryでのドラッグアンドドロップについて 1 2022/07/07 21:04
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
HTML::Templateでutf-8のテンプ...
-
【LaTeX】pBibTeXでのエラーの...
-
sedやperlでの2バイト文字を含...
-
CSVファイルの中で、「 , 」カ...
-
英数字のみ全角から半角に変換
-
エクセルで数値を全角文字(カ...
-
マクロを使ってフォルダー内に...
-
IEからEdgeへの移行に伴うIMEの...
-
カンマ区切りの数字をCSVフ...
-
Excel VBAでPDFファイルをMicro...
-
EXCELからCSVにすると余計なカ...
-
エクセルにMicrosoft Barcode C...
-
各項目がダブルクォーテーショ...
-
ダブルコーテーションの置換
-
csvデータ ダブルクォーテ...
-
VBA 置換文字がみつからない時
-
正規表現で、特定の文字列を含...
-
CString から LPCTSTRの型に変換
-
スペースで区切られた氏名から...
-
プログラミングでは、半角括弧...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
perl でエラー:Wide character...
-
【LaTeX】pBibTeXでのエラーの...
-
MFCのコントロールにUTF-8の文...
-
jcode.plのかわり
-
【文字コード】外見上は全く同...
-
デバッグ情報が文字化けする
-
UTF-16からUTF-8への変換
-
機種依存文字(株)を入力すると\...
-
今更ながらdbmopenでutf8環境に...
-
sedやperlでの2バイト文字を含...
-
perlについての質問
-
CGIでメール送信で件名文字化け
-
Java+MySQLで特殊文字(丸数字(...
-
Jcode.pmでconvertできない
-
WWW::Mechanizeの文字コードに...
-
HTML::Templateでutf-8のテンプ...
-
Perlで、文字の出現回数を調べ...
-
カタカナ一覧表が知りたい
-
WindowsでUTF-8のPerlスクリプ...
-
UTF-8 フラグ?で文字化けしま...
おすすめ情報