使用
时,特殊构造的字符串打印方式不同print $b;
或
print for split //, $b;
最小的例子是:
#!perl
use warnings;
use strict;
use Encode;
my $b = decode 'utf8', "\x{C3}\x{A1}\x{E2}\x{80}\x{93}\x{C3}\x{A1}"; # 'á–á' in Unicode;
print $b, "\n";
print for split //, $b
控制台屏幕上的输出(我想我使用cp860)是:
Wide character in print at xx.pl line 9.
├íÔÇô├í
Wide character in print at xx.pl line 10.
ßÔÇôß
或以十六进制:
C3 A1 E2 80 93 C3 A1
E1 E2 80 93 E1
(当然由0D 0A
分隔,即\r\n
)。
问题是为什么角色的呈现方式不同?
令人惊讶的是,没有em-dash,效果就会消失。对于较长的字符串,可以看到效果,如以下示例所示。
对于字符串'ÉlesmitíoToño-AntonioPérez'(在程序中键入Unicode;请注意两行不同!):
Wide character in print at xx.pl line 14.
├ël es mi t├¡o To├▒o ÔÇôAntonio P├®rez
Wide character in print at xx.pl line 15.
╔l es mi tÝo To±o ÔÇôAntonio PÚrez
然而,对于弦乐'ÉlesmitíoToño,AntonioPérez':
╔l es mi tÝo To±o, Antonio PÚrez
╔l es mi tÝo To±o, Antonio PÚrez
没有什么不好的事情发生,并且两条线以相同的方式呈现。唯一的区别是存在一个短划线–
,即'\x{E2}\x{80}\x{93}'
!
此外,print join '', split //, $b;
与print $b;
的结果相同,但与print for split //, $b;
不同。
如果我添加binmode STDOUT, 'utf8';
,则两个输出均为ÔÇô├í
= E2 80 93 C3 A1。
所以我的问题并不完全是关于如何避免它,而是关于为什么会发生这种情况:为什么相同的字符串在拆分时表现不同?
显然,在这两种情况下都会打开utf8
标志。这是一个更详细的程序,显示有关这两个字符串的更多信息:$a
之前的decode
和$b
之后的decode
:
#!perl
use warnings;
use strict;
use 5.010;
use Encode;
my $a = "\x{C3}\x{A1}\x{E2}\x{80}\x{93}\x{C3}\x{A1}"; # 'á–á' in Unicode;
my $b = decode 'utf8', $a;
say '------- length and utf8 ---------';
say "Length (a)=", length $a, ", is_uft8(a)=", (Encode::is_utf8 ($a) // 'no'), ".";
say "Length (b)=", length $b, ", is_uft8(b)=", (Encode::is_utf8 ($b) // 'no'), ".";
say '------- as a variable---------';
say "a: $a";
say "b: $b", ' <== *** WHY?! ***';
say '------- split ---------';
print "a: "; print for split //, $a; say '';
print "b: "; print for split //, $b; say ' <== *** DIFFERENT! ***';
say '------- split with spaces ---------';
print "a: "; print "[$_] " for split //, $a; say '';
print "b: "; print "[$_] " for split //, $b; say '';
say '------- split with properties ---------';
print "a: "; print "[$_ is_utf=" . Encode::is_utf8 ($_) . " length=" . length ($_) . "] " for split //, $a; say '';
print "b: "; print "[$_ is_utf=" . Encode::is_utf8 ($_) . " length=" . length ($_) . "] " for split //, $b; say '';
say '------- ord() ---------';
print "a: "; print ord, " " for split //, $a; say '';
print "b: "; print ord, " " for split //, $b; say '';
,这是控制台上的输出:
------- length and utf8 ---------
Length (a)=7, is_uft8(a)=.
Length (b)=3, is_uft8(b)=1.
------- as a variable---------
a: ├íÔÇô├í
Wide character in say at x.pl line 16.
b: ├íÔÇô├í <== *** WHY?! ***
------- split ---------
a: ├íÔÇô├í
Wide character in print at x.pl line 19.
b: ßÔÇôß <== *** DIFFERENT! ***
------- split with spaces ---------
a: [├] [í] [Ô] [Ç] [ô] [├] [í]
Wide character in print at x.pl line 22.
b: [ß] [ÔÇô] [ß]
------- split with properties ---------
a: [├ is_utf= length=1] [í is_utf= length=1] [Ô is_utf= length=1] [Ç is_utf= length=1] [ô is_utf= length=1] [├ is_utf= length=1] [í is_utf= length=1]
Wide character in print at x.pl line 25.
b: [ß is_utf=1 length=1] [ÔÇô is_utf=1 length=1] [ß is_utf=1 length=1]
------- ord() ---------
a: 195 161 226 128 147 195 161
b: 225 8211 225
答案 0 :(得分:4)
不同之处在于正在打印的字符串是否包含任何> 255的字符。 print
只知道你在那种情况下做错了什么 [1] 。
给定一个没有:encoding
的句柄,print
需要一个字节串(字符串≤255)。
当它没有接收到字节时(字符串包含字符&gt; 255),它会通知您错误(“宽字符”)并猜测您打算使用UTF-8对字符串进行编码。
您可以在没有print
的句柄上考虑:encoding
执行以下操作:
if ($s =~ /[^\x00-\xFF]/) {
warn("Wide character");
utf8::encode($s);
}
my $b = decode 'utf8', "\x{C3}\x{A1}\x{E2}\x{80}\x{93}\x{C3}\x{A1}";
与
相同my $b = "\xE1\x{2013}\xE1";
因此,你正在做
print "\xE1\x{2013}\xE1";
print "\xE1";
print "\x{2013}";
print "\xE1";
print "\xE1\x{2013}\xE1"; # Wide char! C3 A1 E2 80 93 C3 A1
Perl注意到您忘记编码,警告您,并打印使用UTF-8编码的字符串。
print "\xE1"; # E1
Perl无法知道您忘记编码,因此它会打印您要求打印的内容。
print "\x{2013}"; # Wide char! E2 80 93
Perl注意到您忘记编码,警告您,并打印使用UTF-8编码的字符串。
脚注
存储格式的选择(由is_utf8
返回)永远不会产生影响。 print
正确地不受其影响。
utf8::downgrade( my $d = chr(0xE1) ); print($d); # UTF8=0 prints E1
utf8::upgrade( my $u = chr(0xE1) ); print($u); # UTF8=1 prints E1