9

我找不到关于如何在 Perl 中存储字符串数据的基本描述!就像所有文档都假设我出于某种原因已经知道这一点。我知道 encode()、decode(),而且我知道我可以将原始字节读入 Perl“字符串”并再次输出它们,而无需 Perl 搞砸它们。我知道开放模式。我还收集到 Perl 必须使用一些内部格式来存储字符串,并且可以区分字符和二进制数据。请问这是在哪里记录的???

等效的问题是;给定这个 perl:

$x = decode($y);

解码到什么和从什么?

据我所知,字符串数据结构上必须有一个标志,表示这是二进制 XOR 字符数据(BTW 是 Unicode 的超集的某种内部格式 - http://perldoc.perl.org/Encode.html #描述)。但是,如果在文档中说明或在此处确认/不可信,我会喜欢它。

4

5 回答 5

18

这是一个很好的问题。为了进行调查,我们可以通过使用Devel::Peek来更深入地了解我们的字符串(或其他变量)中实际存储的内容。

首先让我们从一个 ASCII 字符串开始

$ perl -MDevel::Peek -E 'Dump "string"'
SV = PV(0x9688158) at 0x969ac30
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK)
  PV = 0x969ea20 "string"\0
  CUR = 6
  LEN = 12

然后我们可以打开unicode IO层并做同样的事情

$ perl -MDevel::Peek -CSAD -E 'Dump "string"'
SV = PV(0x9eea178) at 0x9efcce0
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK)
  PV = 0x9f0faf8 "string"\0
  CUR = 6
  LEN = 12

从那里让我们尝试手动添加一些宽字符

$ perl -MDevel::Peek -CSAD -e 'Dump "string \x{2665}"'
SV = PV(0x9be1148) at 0x9bf3c08
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK,UTF8)
  PV = 0x9bf7178 "string \342\231\245"\0 [UTF8 "string \x{2665}"]
  CUR = 10
  LEN = 12

从中可以清楚地看到 Perl 已将其正确解释为 utf8。问题是,如果我不使用\x{}转义来提供八位字节,则表示看起来更像是常规字符串

$ perl -MDevel::Peek -CSAD -E 'Dump "string ♥"'
SV = PV(0x9143058) at 0x9155cd0
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK)
  PV = 0x9168af8 "string \342\231\245"\0
  CUR = 10
  LEN = 12

Perl 看到的只是字节,并且无法知道您的意思是它们是 unicode 字符,这与您在上面输入转义八位字节时不同。现在让我们使用decode看看会发生什么

$ perl -MDevel::Peek -CSAD -MEncode=decode -E 'Dump decode "utf8", "string ♥"'
SV = PV(0x8681100) at 0x8683068
  REFCNT = 1
  FLAGS = (TEMP,POK,pPOK,UTF8)
  PV = 0x869dbf0 "string \342\231\245"\0 [UTF8 "string \x{2665}"]
  CUR = 10
  LEN = 12

TADA!,现在您可以看到字符串在内部正确表示,与您使用\x{}转义时输入的内容匹配。

实际的答案是它是从字节到字符的“解码”,但我认为当你看到 Peek 输出时它更有意义。

最后,您可以使用utf8pragma 让 Perl 将源代码视为 utf8,如下所示

$ perl -MDevel::Peek -CSAD -Mutf8 -E 'Dump "string ♥"'
SV = PV(0x8781170) at 0x8793d00
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK,UTF8)
  PV = 0x87973b8 "string \342\231\245"\0 [UTF8 "string \x{2665}"]
  CUR = 10
  LEN = 12
于 2013-03-02T06:04:25.947 回答
6

与其标量变量的流动字符串/数字状态不同,Perl 字符串的内部格式是可变的并且取决于字符串的内容。

看一下perluniintro,上面写着这个。

在内部,Perl 当前使用平台的本机 8 位字符集(例如 Latin-1)(默认为 UTF-8)来编码 Unicode 字符串。具体来说,如果字符串中的所有代码点都是 0xFF 或更小,Perl 使用本机 8 位字符集。否则,它使用 UTF-8。

这意味着像这样的字符串"I have £ two"存储为 (bytes) I have \x{A3} two。(井号是U+00A3。)现在,如果我附加一个多字节 unicode 字符串,例如U+263A- 一个笑脸 - Perl 将在附加新字符之前将整个字符串转换为 UTF-8,给出 (bytes) I have \xC2\xA3 two\xE2\x98\xBA。再次删除最后一个字符会使字符串 UTF-8 编码为`I have \xC2\xA3 two.

但我想知道为什么你需要知道这一点。除非您使用 C 编写 XS 扩展,否则内部格式对您来说是透明且不可见的。

于 2013-03-02T06:21:43.300 回答
2

Perls 内部字符串格式取决于实现,但通常是 UtF-8 的超集。它是什么并不重要,因为您使用 decode 和 encode 将字符串转换为内部格式以及从内部格式转换为其他编码。

解码转换为 perls 内部格式,编码转换为 perls 内部格式。

二进制数据的内部存储方式与字符 0 到 255 的存储方式相同。

编码和解码只是在格式之间转换。例如,UTF8 编码意味着每个字符只能是使用 perl 字符 vlaues 0 到 255 的八位字节,即字符串由 UTF8 八位字节组成。

于 2013-03-02T06:03:24.163 回答
2

简短的回答:一团糟
稍微长一点:程序员看不到差异。

基本上,您必须记住您的字符串是否包含字节或字符,其中字符是 unicode 代码点。如果只遇到ASCII,区别是看不到的,很危险。

数据本身和此类数据的表示是不同的,不应混淆。字符串(在概念上)是一系列代码点,但在内存中表示为字节数组,并在encoded. 如果要将二进制数据存储在字符串中,则将代码点的数量重新解释为字节值,并将自己限制在 0-255 之间的代码点。

(例如,一个文件没有编码。该文件中的信息有一些编码(在字符级别是 ASCII、UTF-16 或 EBCDIC,在应用程序级别是 Perl、HTML 或 .ini))

字符串的确切存储格式无关紧要,但您可以在这样的字符串中存储完整的整数:

# this will work if your perl was compiled with large integers
my $string = chr 2**64; # this is so not unicode
say ord $string; # 18446744073709551615

内部格式会相应调整以适应这些值;普通字符串不会占用每个字符一个整数。

于 2013-03-02T06:20:59.877 回答
-2

Perl 可以处理比 Unicode 更多的东西,所以它非常灵活。有时您想与无法连接的东西进行交互,因此您可以使用 encode(...) 和 decode(...) 处理这些转换。见http://perldoc.perl.org/utf8.html

于 2013-03-02T05:15:33.313 回答