Perl中用于确定值是字节序列还是编码字符串的标准测试是什么?如果它是一个编码字符串,它的字符编码是什么?
让我们假设以下完整的Perl脚本:
'foo';
如何确定这个文字字符串是一个字节序列还是某些编码中的字符串?如果它是某些字符编码中的字符串,那么它的字符编码是什么?
这个问题与Unicode或UTF-8无关。它通常与Perl中的字节与字符有关。这个问题也与自动字符编码检测无关,这完全是一个不同的主题。
更新
在初始化$letter
之后,我希望Perl告诉我它认为存储在变量$letter
中的字母的字符编码是什么。我不认为它一定是对< / em>的。确保Perl理解字母编码的字符是我作为程序员的责任。我明白了。但是应该有一种简单,简单的方法来测试Perl认为字符(或字符串)所在的字符编码。不存在吗?
C:\>perl -E "$letter = 'Ž'; say $letter =~ m/\w/ ? 'matches' : 'does not match'"
does not match
C:\>perl -MEncode -E "$letter = decode('UTF-8', 'Ž'); say $letter =~ m/\w/ ? 'matches' : 'does not match'"
does not match
C:\>perl -MEncode -E "$letter = decode('Windows-1252', 'Ž'); say $letter =~ m/\w/ ? 'matches' : 'does not match'"
matches
C:\>perl -MEncode -E "$letter = decode('Windows-1252', 'Ž'); $letter = encode('Windows-1252', $letter); say $letter =~ m/\w/ ? 'matches' : 'does not match'"
does not match
C:\>chcp
Active code page: 1252
C:\>
Perl无法按需报告理解(正确或错误)<{1}}中存储的值所在的字符编码?
答案 0 :(得分:6)
与其他一些编程语言(如Python)不同,Perl不区分“字节字符串”和“Unicode字符串”。所有字符串都具有Unicode语义以及字节语义。
话虽如此,在包含ASCII,ISO8859-1或二进制数据的字符串与包含Unicode数据的字符串之间存在纯内部区别。这种区别是使用UTF8标志进行的,可以使用utf8::is_utf8()
函数进行检查。但是,请记住,此标志会自动设置和清除 - 例如,将非ISO-8859-1字符(例如,™
)附加到字符串将重新编码字符串中的任何数据为UTF- 8,如有必要,设置UTF8标志。但是,这种转换对于纯Perl程序是不可见的,因此您很少需要查看它。
如果你有一个非Unicode字符串(例如二进制数据),你需要弄清楚它是什么编码,请参阅How can I guess the encoding of a string in Perl?。
答案 1 :(得分:0)
没有未编码的文件。 Perl编程语言假定源文件是Latin-1或其他东西。这是一个单字节编码,因此字符和八位字节之间存在1:1的映射。这意味着在使用UTF-8编码保存的文件中
length("ø") == 2 and
"ø" eq "\xc3\xb8" and
"ø" ne "\N{LATIN SMALL LETTER O WITH STROKE}"
use utf8
下的所有内容都不正确。
在Perl中,每个字符串都是一系列代码点。如上所述,在没有任何解码步骤的情况下,每个八位字节将被视为一个代码点。这适用于源文件中的字符串文字和没有PerlIO层的IO操作。
encode
函数接受一串代码点,并使用指定的编码对其进行编码。 E.g。
use utf8;
use Test::More; use Encode;
# "is" tests for string equality, "isnt" is the negation
my $str = "ø";
isnt $str, "\xc3\xb8", "String is unencoded";
is length($str), 1, "Unencoded char has length 1";
my $encoded = encode "UTF-8", $str;
is $encoded, "\xc3\xb8", "The string is properly encoded";
is length($encoded), 2, "Encoding may map a codepoint to multiple bytes";
这会发出一个字节字符串,表示为0x00-0xFF范围内的代码点。编码的字符串没有可以查询的编码;你,程序员,必须知道它。因为它只是一个普通的字符串,我们可以再次编码:
my $double_encoded = encode "UTF-8", $encoded;
is $double_encoded, "\xc3\x83\xc2\xb8", "Double encoding works without type error";
decode
函数在字节范围(也称为字节字符串)中获取一串代码点,并根据相应编码的规则对其进行转换。所以:
is decode("utf8", $double_encoded), $encoded, "Decoding works";
is decode("utf8", $encoded), $str, "Decoding works 2";
它反转编码步骤,从而可能将多个字节范围的字符映射到单个代码点。
done_testing;
答案 2 :(得分:0)
它通常与Perl中的字节与字符有关。
这没有任何意义。根据定义,字符串的每个元素都是一个字符,所以它肯定是一串字符。
字符也可以是字节(8位值)。这不是一个或两件事。
如何确定这个文字字符串是一个字节序列还是某些编码中的字符串?
您有一个由字符66,6F和6F组成的字符串。 Perl如何知道这些值代表什么?它们是Unicode代码点吗?它们是使用UTF-8进行HTML编码的吗?它们是使用UTF-8的配置文件吗?他们是温度传感器测量?它无从知晓。它们只是三个值。
答案 3 :(得分:0)
'Ž'
与chr(0x8E)
相同。
记住以下内容,
decode('UTF-8', chr(0x8E)) === chr(0xFFFD) [Invalid UTF-8]
decode('cp1252', chr(0x8E)) === chr(0x17D)
encode('cp1252', chr(0x17D)) === chr(0x8E)
您的第一个代码段将0x8E传递给匹配运算符。 U + 008E(SINGLE SHIFT TWO)不是“字”代码点。
您所看到的是将Unicode代码点以外的内容(cp1252编码文本)传递给期望Unicode代码点的运算符的效果。
您的第二个代码段将0xFFFD传递给匹配运算符。 U + FFFD(REPLACEMENT CHARACTER)不是“单词”代码点。
您所看到的是将UTF-8编码文本(cp1252编码文本)以外的内容传递给期望UTF-8的函数。
您的第三个代码段将0x017D传递给匹配运算符。 U + 017D(带卡通的拉丁文大写字母Z)是一个“字”代码点。
您的第四个代码段与第一个代码段一样,将0x8E传递给匹配运算符。
您所看到的是将Unicode代码点以外的内容(cp1252编码文本)传递给期望Unicode代码点的运算符的效果。
您的更新实际上演示了之前的答案已经告诉您:匹配运算符始终认为字符串是一串代码点。没有什么要检查,因为行为始终是相同的。
(关于“语义”的段落与您的更新无关。由于-E
,总是获得正确的行为。)
答案 4 :(得分:-3)
Perl缺少一种简单的方法来知道假定字符串的字符编码是什么。它有一个内部标志可以被探测以确定它自己的内部字符串表示是否是UTF-8,但是这与确定一串字符的字符编码的测试完全不同。
让我们设想一个名为encoding()的名义内置函数。这是它的作用:
C:\>perl -E "say encoding 'quick brown fox'"
ISO-8859-1
C:\>perl -E "use utf8; say encoding 'quick brown fox'"
UTF-8
C:\>perl -E "use utf8; say encoding 'γρήγορη καφέ αλεπού'"
UTF-8
C:\>perl -Mutf8 -MEncode -E "say encoding decode('ISO-8859-7', 'γρήγορη καφέ αλεπού')"
ISO-8859-7
C:\>
(默认字符编码为ISO-8859-1,也称为拉丁文1。)
这真的不像其他人所说的那样难以回答,这正是其中的重点。如果Perl有一个内置函数来将字符编码分配报告给一串字符,那么它将有助于理解,讨论和处理不同的字符编码。