在 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
(在内部,一个名为“utf8”的 UTF-8 扩展用于存储 72 位字符的字符串。除了意识到性能影响外,这不是你应该知道的,但有一些错误暴露了这一事实。 )
编码is_utf8
报告标量包含哪种类型的字符串。这是一个除了调试我之前提到的错误之外完全没有用的功能。
- 8 位字符串可以存储
"abc"
(或 OP 中的字符串$str
)的值,因此 Perl 使用更有效的 8 位 (UTF8=0) 字符串格式。
- 8 位字符串不能存储
"\x{2660}\x{2661}"
(或 OP 中的字符串$string
)的值,因此 Perl 使用 72 位 (UTF8=1) 字符串格式。
无论是存储在浮点数、有符号整数还是无符号整数中,零都是零。类似地,字符串的存储格式不传达有关字符串值的信息。
- 您可以像 72 位字符串一样轻松地将代码点存储在 8 位字符串中(如果它们足够小的话)。
- 您可以像存储 8 位字符串一样轻松地将字节存储在 72 位字符串中。
实际上,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