String 在Perl中,为什么utf-8字符串在拆分为字符时打印方式不同?

String 在Perl中,为什么utf-8字符串在拆分为字符时打印方式不同?,string,perl,unicode,utf-8,language-lawyer,String,Perl,Unicode,Utf 8,Language Lawyer,使用时,特殊构造的字符串的打印方式不同 print $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)是: 或十六进制

使用时,特殊构造的字符串的打印方式不同

print $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)是:

或十六进制:

C3 A1 E2 80 93 C3 A1 
E1 E2 80 93 E1
(当然由
0D 0A
分隔,即
\r\n

问题是为什么角色的呈现方式不同

令人惊讶的是,没有em破折号,效果就消失了。对于较长的字符串可以看到这种效果,如下例所示

对于字符串“Éles mi tío Toño–Antonio pérez”(在程序中键入为Unicode;注意这两行是不同的!):

然而,对于字符串“El es mi tío Toño,Antonio pé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}'

另外,
打印联接“”,拆分/,$b
给出与打印$b相同的结果但与拆分打印不同,$b

如果我添加
binmode标准输出'utf8',则两个输出都是
á
=E2 80 93 C3 A1

所以我的问题不是如何避免,而是为什么会发生这种情况:为什么同一个字符串在拆分时表现不同

显然,在这两种情况下,
utf8
标志都处于启用状态。下面是一个更详细的程序,它显示了有关这两个字符串的更多信息:
解码之前的
$a
解码之后的
$b

#!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 '';
#!perl
使用警告;
严格使用;
使用5.010;
使用编码;
my$a=“\x{C3}\x{A1}\x{E2}\x{80}\x{93}\x{C3}\x{A1}”;#Unicode中的á–á;
my$b=解码'utf8',$a;
说出'----长度和utf8---';
说“长度(a)=”,长度$a,”,is_uft8(a)=”,(编码::is_utf8($a)/“否”),“;
说“长度(b)=”,长度$b,”,is_uft8(b)=”,(编码::is_utf8($b)/‘否’),“;
把“-----作为变量----------”;
说“a:$a”;

说“b:$b”,区别在于所打印的字符串是否包含任何大于255的字符<代码>打印
只知道在那种情况下你做错了什么[1]


给定一个没有
:encoding
的句柄,
print
需要一个字节字符串(字符串≤255)

当它不接收字节(字符串包含大于255个字符)时,它会通知您错误(“宽字符”),并猜测您打算使用UTF-8对字符串进行编码

您可以将不带
的手柄上的
打印
:编码
视为执行以下操作:

if ($s =~ /[^\x00-\xFF]/) {
   warn("Wide character");
   utf8::encode($s);
}

my $b = "\xE1\x{2013}\xE1";
因此,你正在做什么

print "\xE1\x{2013}\xE1";
print "\xE1";
print "\x{2013}";
print "\xE1";
  • Perl注意到您忘记编码,警告您,并打印使用UTF-8编码的字符串

  • Perl无法知道您忘记了编码,所以它会打印您要求它打印的内容

  • Perl注意到您忘记编码,警告您,并打印使用UTF-8编码的字符串


  • 脚注

  • 存储格式的选择(由
    返回的是_utf8
    )不应产生任何影响<代码>打印
  • 正确地不受其影响

    utf8::downgrade( my $d = chr(0xE1) );  print($d);  # UTF8=0 prints E1
    utf8::upgrade(   my $u = chr(0xE1) );  print($u);  # UTF8=1 prints E1
    

    请避免使用
    my$a
    my$b
    。它可以搞乱
    排序
    和一些常用的库sub。是的,对!我使用它们只是为了便于阅读:-)在真实的程序中不能使用。啊哈,现在我明白了!非常简单:
    print
    将整个字符串解释为
    utf8
    ,如果该字符串至少包含一个ASCII范围以上的字符。当我拆分字符串时,
    print
    仅将ASCII范围以上的单个字符(1个字符的字符串)解释为utf8,而将其他字符解释为ASCII。
    print
    将整个字符串解释为需要编码的Unicode码点,前提是该字符串至少包含255以上的一个字符。(UTF-8是它的结尾,而不是它的开头。ASCII编码只有128个字符,而且
    print
    并不像提到ASCII那样假定字符是文本。)在我理解你的意思之前,我读了几百遍。默认情况下,perl不会打印出任何大于255的代码点,因此它会将整个字符串转换回字节(代码点Re“默认情况下,perl不会打印出任何大于255的代码点”,正确。它不能。文件只能包含字节。除字节外的任何内容都需要转换为字节。
    :编码告诉perl如何执行此操作。Re“我认为按原样打印代码点是有意义的。”这没有意义。0x2660不能放在文件中。
    
    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
    
    print "\xE1";               # E1
    
    print "\x{2013}";           # Wide char! E2 80 93
    
    utf8::downgrade( my $d = chr(0xE1) );  print($d);  # UTF8=0 prints E1
    utf8::upgrade(   my $u = chr(0xE1) );  print($u);  # UTF8=1 prints E1