Perl的默认字符串编码和表示

Cra*_*lus 5 string perl utf-8 character-encoding

在下面的:

my $string = "Can you \x{FB01}nd my r\x{E9}sum\x{E9}?\n";

x{FB01}x{E9}是代码点.并且代码点通过编码方案编码为一系列八位字节.
所以è具有代码点的字符\x{FB01}是字符串的一部分$string.但这是如何工作的?是所有的人物在这句话(包括ASCII的)通过编码UTF-8
如果是,为什么我会得到以下行为?

my $str = "Some arbitrary string\n";  

if(Encode::is_utf8($str)) {  
        print "YES str IS UTF8!\n";  
}  
else {  
        print "NO str IT IS NOT UTF8\n";   
}  
Run Code Online (Sandbox Code Playgroud)

这打印"NO str IT IS NOT UTF8\n"
另外Encode::is_utf8($string)返回true.
以什么方式$string$str不同而一个被考虑UTF-8而另一个没有?
而且无论如何编码是$str什么?ASCII?这是默认设置Perl吗?

ike*_*ami 8

在C中,字符串是八位字节的集合,但Perl有两种​​字符串存储格式:

  • 8位值的字符串.
  • 72位值的字符串.(实际上,限制为32位或64位.)

因此,您无需对代码点进行编码即可将其存储在字符串中.

my $s = "\x{2660}\x{2661}";
say length $s;                            # 2
say sprintf '%X', ord substr($s, 0, 1);   # 2660
say sprintf '%X', ord substr($s, 1, 1);   # 2661
Run Code Online (Sandbox Code Playgroud)

(在内部,UTF-8的扩展名为"utf8"用于存储72位字符串.除了意识到性能影响之外,这不是你应该知道的事情,但是有些错误暴露了这个事实. )

编码is_utf8报告标量包含的字符串类型.除了调试我之前提到的错误之外,它是一个绝对没用的功能.

  • 8位字符串可以存储"abc"(或者是OP中的字符串$str)的值,因此Perl使用更高效的8位(UTF8 = 0)字符串格式.
  • 8位字符串不能存储"\x{2660}\x{2661}"(或OP中的字符串$string)的值,因此Perl使用72位(UTF8 = 1)字符串格式.

无论是存储在浮点数,有符号整数还是无符号整数中,零都为零.类似地,字符串的存储格式不传达有关字符串值的信息.

  • 您可以将代码点存储为8位字符串(如果它们足够小),就像72位字符串一样容易.
  • 您可以将字节存储在72位字符串中,就像8位字符串一样容易.

事实上,Perl将随意在两种格式之间切换.例如,如果您连接$string使用$str,你会得到在72位格式的字符串.

你可以改变与内建的字符串的存储格式utf8::downgradeutf8::upgrade,你应该永远需要解决的错误.

utf8::downgrade($s);  # Switch to strings of  8-bit values (UTF8=0).
utf8::upgrade($s);    # Switch to strings of 72-bit values (UTF8=1).
Run Code Online (Sandbox Code Playgroud)

您可以使用Devel :: Peek查看效果.

>perl -MDevel::Peek -e"$s=chr(0x80); utf8::downgrade($s); Dump($s);"
SV = PV(0x7b8a74) at 0x4a84c4
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x7bab9c "\200"\0
  CUR = 1
  LEN = 12

>perl -MDevel::Peek -e"$s=chr(0x80); utf8::upgrade($s); Dump($s);"
SV = PV(0x558a6c) at 0x1cc843c
  REFCNT = 1
  FLAGS = (POK,pPOK,UTF8)
  PV = 0x55ab94 "\302\200"\0 [UTF8 "\x{80}"]
  CUR = 2
  LEN = 12
Run Code Online (Sandbox Code Playgroud)

  • 我认为将Perl视为内部"一些默认编码"的字符串,而不是"在一些内部表示中",这只会让更多人感到困惑而不是它有所帮助.最好将字符串视为逻辑代码点的序列.我相信知道这个逻辑字符串的确切内存布局对于万分之一的人来说是有帮助的,并且对大多数其他人都有害. (4认同)

dax*_*xim 5

\ x {FB01}和\ x {E9}是代码点.

不安静,大括号内的数值是代码点.整个\ x表达式只是一个字符的表示法.字符有几种符号,大多数以反斜杠开头,但常见的是简单的字符串文字.你不妨写一下:

use utf8;
my $string = "Can you ?nd my résumé?\n";
#                     ?       ?   ?
Run Code Online (Sandbox Code Playgroud)

并且代码点通过编码方案编码为一系列八位字节.

是的,但到目前为止,你的字符串是一串字符,而不是八位字节的缓冲区.

但这是如何工作的?

字符串由字符组成.这只是Perl的模型.作为程序员,你应该在这个级别处理它.

当然,计算机不能,而内部数据结构必须具有某种形式的内部编码.由于"Perl不能保守秘密",所以会产生太多混乱,细节偶尔会泄露出来.

这句话中的所有字符(包括ASCII字符)都是通过UTF-8编码的吗?

不,内部编码是松散的UTF8(没有破折号).它没有UTF-8(又名UTF-8-strict)的一些限制.

  1. UTF-8上升到0x10_ffff,UTF8上升到我的64位系统上的0xffff_ffff_ffff_ffff.但是,大于0xffff_ffff的代码点将发出不可移植性警告.
  2. 在UTF-8中,某些代码点是非字符或非法字符.在UTF8中,任何事情都会发生.

编码:: is_utf8

...是一个内部功能,并清楚地标记为这样.你作为程序员不应该偷看.但既然你想要偷看,没有人可以阻止你.Devel :: Peek :: Dump是一个更好的工具,可以进入内部.

阅读http://p3rl.org/UNI,了解Perl中的编码主题.