5

在下面的:

my $string = "Can you \x{FB01}nd my r\x{E9}sum\x{E9}?\n";

和是代码点x{FB01}x{E9}并且代码点通过编码方案编码为一系列八位位组。
所以è具有代码点的字符\x{FB01}是字符串的一部分$string。但这是如何工作的?这句话中的所有字符(包括 ASCII 字符)都是通过 ? 编码的UTF-8
如果是,为什么我会得到以下行为?

my $str = "Some arbitrary string\n";  

if(Encode::is_utf8($str)) {  
        print "YES str IS UTF8!\n";  
}  
else {  
        print "NO str IT IS NOT UTF8\n";   
}  

这将打印"NO str IT IS NOT UTF8\n"
另外Encode::is_utf8($string)返回true
有哪些不同之处,一种被考虑$string,另一种不被考虑? 无论如何,编码是什么?ASCII?这是默认的吗?$strUTF-8
$strPerl

4

3 回答 3

8

在 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
于 2013-06-20T20:24:58.550 回答
5

\x{FB01} 和 \x{E9} 是代码点。

不安静,大括号内的数值是代码点。整个 \x 表达式只是一个字符的符号。字符有多种表示法,其中大多数以反斜杠开头,但常见的一种是简单的字符串文字。你不妨写:

use utf8;
my $string = "Can you find my résumé?\n";
#                     ↑       ↑   ↑

并且代码点通过编码方案编码为一系列八位位组。

是的,但到目前为止,您的字符串是一串字符,而不是八位字节的缓冲区。

但这是如何工作的?

字符串由字符组成。这只是 Perl 的模型。作为程序员,你应该在这个级别处理它。

当然,计算机不能,内部数据结构必须有某种形式的内部编码。由于“Perl 不能保守秘密” ,导致了太多的混乱,细节偶尔会泄露出去。

这句话中的所有字符(包括 ASCII 字符)都是通过 UTF-8 编码的吗?

不,内部编码是宽松的 UTF8(没有破折号)。它没有 UTF-8(又名 UTF-8-strict)所具有的一些限制。

  1. 在我的 64 位系统上,UTF-8 最高为 0x10_ffff,UTF8 最高为 0xffff_ffff_ffff_ffff。但是,大于 0xffff_ffff 的代码点将发出不可移植警告。
  2. 在 UTF-8 中,某些代码点是非字符或非法字符。在 UTF8 中,任何事情都会发生。

编码::is_utf8

… 是一个内部函数,并明确标记为。作为程序员的你不应该偷看。但既然你想偷看,没有人可以阻止你。Devel::Peek::Dump是了解内部结构的更好工具。

阅读http://p3rl.org/UNI ,了解Perl 编码主题的介绍。

于 2013-06-20T20:42:28.267 回答
3

is_utf8是一个名字不好的函数,它并不意味着你认为它意味着什么或与它有任何关系。您的问题的答案是$string没有编码,因为它没有编码。当您Encode::encode使用某种编码调用时,其结果将是一个已编码的字符串,并且具有已知的编码

于 2013-06-20T20:33:59.073 回答