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
内部使用UTF-8的扩展"utf8"来存储72位字符的字符串。这不是你必须知道的事情,除了意识到性能影响之外,但有些错误会暴露这个事实。
Encode的"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::downgrade
和utf8::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).
你可以使用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