2

Perl 中确定一个值是字节序列还是编码字符串的标准测试是什么?如果它是一个编码字符串,它是什么字符编码?

让我们假设以下完整的 Perl 脚本:

'foo';

如何确定这个文字字符串是字节序列还是某种编码的字符串?如果它是某种字符编码的字符串,它是什么字符编码?

这个问题与 Unicode 或 UTF-8 无关。通常是关于 Perl 中的字节与字符。这个问题也不是关于自动字符编码检测,这完全是一个不同的话题。

更新

初始化后$letter,我希望 Perl 告诉我它认为存储在变量中的字母在什么字符编码$letter中。我不希望它一定是正确的。确保 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 不能按需报告它理解(正确或错误)存储的值的字符编码$letter是什么?

4

5 回答 5

6
于 2013-07-08T00:44:43.977 回答
0

通常是关于 Perl 中的字节与字符。

这是没有意义的。根据定义,字符串的每个元素都是一个字符,因此它肯定是一个字符串。

字符也可以是字节(8 位值)。这不是非此即彼的事情。

如何确定这个文字字符串是字节序列还是某种编码的字符串?

您有一个由字符 66、6F 和 6F 组成的字符串。Perl 假设如何知道这些值代表什么?它们是 Unicode 代码点吗?它们是使用 UTF-8 编码的 HTML 吗?它们是使用 UTF-8 的配置文件吗?它们是温度传感器测量值吗?它没有办法知道。它们只是三个值。

于 2013-07-08T03:44:26.997 回答
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函数采用一串代码点并使用指定的编码对其进行编码。例如

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;
于 2013-07-07T23:52:11.653 回答
0

cp1252中的“Ž”是8E,所以你感知到'Ž'的和chr(0x8E).

牢记这一点和以下几点,

decode('UTF-8', chr(0x8E))     ===   chr(0xFFFD)  [Invalid UTF-8]
decode('cp1252', chr(0x8E))    ===   chr(0x17D)
encode('cp1252', chr(0x17D))   ===   chr(0x8E)
  1. 您的第一个片段将 0x8E 传递给匹配运算符。U+008E (SINGLE SHIFT TWO) 不是“字”代码点。

    您所看到的是将 Unicode 代码点(cp1252 编码的文本)以外的内容传递给期望 Unicode 代码点的运算符的效果。

  2. 您的第二个片段将 0xFFFD 传递给匹配运算符。U+FFFD(替换字符)不是“字”代码点。

    您所看到的是将 UTF-8 编码文本(cp1252 编码文本)以外的内容传递给需要 UTF-8 的函数的效果。

  3. 您的第三个片段将 0x017D 传递给匹配运算符。U+017D(带有 CARON 的拉丁文大写字母 Z)是一个“单词”代码点。

  4. 您的第四个片段,就像您的第一个片段一样,将 0x8E 传递给匹配运算符。

    您所看到的是将 Unicode 代码点(cp1252 编码的文本)以外的内容传递给期望 Unicode 代码点的运算符的效果。

您的更新实际上展示了以前的答案已经告诉您的内容:匹配运算符始终将字符串视为代码点字符串。没有什么要检查的,因为行为总是相同的。

(关于“语义”的段落与您的更新无关。由于 .),始终会获得正确的行为-E。)

于 2013-07-08T04:54:07.403 回答
-2

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 有一个内置函数来报告分配给字符串的字符编码,它将有助于更容易理解、讨论和处理不同的字符编码。

于 2013-07-08T05:00:40.200 回答