我找不到关于如何在Perl中存储字符串数据的基本描述!就像所有的文档假设我已经知道这个由于某种原因。我知道encode(),decode(),我知道我可以将原始字节读入Perl“字符串”并再次输出它们而不用Perl搞砸它们。我知道开放模式。我也收集Perl必须使用一些内部格式来存储字符串,并可以区分字符和二进制数据。请记录在哪里???
等效问题是;鉴于此perl:
$x = decode($y);
解码为什么和什么?
据我所知,字符串数据结构上必须有一个标志,表示这是二进制XOR字符数据(某些内部格式,BTW是Unicode的超集 - http://perldoc.perl.org/Encode.html#DESCRIPTION)。但如果在文档中说明或在此确认/声名狼借,我会喜欢它。
答案 0 :(得分:15)
这是一个很好的问题。为了进行调查,我们可以使用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输出时更有意义。
最后,您可以使用utf8
编译指示使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
答案 1 :(得分:6)
与其标量变量的流体字符串/数字状态相似,Perl字符串的内部格式是可变的,取决于字符串的内容。
看看perluniintro
,说明了这一点。
在内部,Perl目前使用平台的原生八位字符集(例如Latin-1),默认为UTF-8,来编码Unicode字符串。具体来说,如果字符串中的所有代码点都是0xFF或更小,则Perl使用本机8位字符集。否则,它使用UTF-8。
这意味着像"I have £ two"
这样的字符串存储为(字节)I have \x{A3} two
。 (英镑符号为U+00A3
。)现在,如果我附加一个多字节的unicode字符串,例如U+263A
- 一个笑脸 - Perl会在添加新字符之前将整个字符串转换为UTF-8 ,给(字节)I have \xC2\xA3 two\xE2\x98\xBA
。删除最后一个字符会使字符串UTF-8编码为`I have \xC2\xA3 two
。
但我想知道为什么你需要知道这一点。除非您在C中编写XS扩展,否则内部格式是透明的,对您来说是不可见的。
答案 2 :(得分:3)
简短的回答:这是一团糟 稍微长一点:程序员看不到差异。
基本上你必须记住你的字符串是否包含字节或字符,其中字符是unicode代码点。如果您只遇到ASCII,则差异是不可见的,这很危险。
数据本身和此类数据的表示是截然不同的,不应混淆。字符串(概念上)是一系列代码点,但在内存中表示为字节数组,在encode
d时表示为某个字节序列。如果要将二进制数据存储在字符串中,则将代码点的编号重新解释为字节值,并将自己限制为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
相应调整内部格式以适应这些值;普通字符串不会占用每个字符一个整数。
答案 3 :(得分:2)
Perls内部字符串格式是依赖于实现的,但通常是UtF-8的超级集合。它是什么并不重要,因为你使用解码和编码将字符串转换为内部格式和从内部格式转换为其他编码。
解码转换为perls内部格式,编码从perls内部格式转换。
二进制数据以与字符0到255相同的方式存储在内部。
编码和解码只是在格式之间转换。例如,UTF8编码意味着每个字符只使用perl字符0到255的八位字节,即该字符串由UTF8八位字节组成。
答案 4 :(得分:-2)
Perl可以处理的不仅仅是Unicode,所以它非常灵活。有时你想要与不能的东西接口,所以你可以使用encode(...)和decode(...)处理那些转换。见http://perldoc.perl.org/utf8.html