Perl的默认字符串编码和表示

时间:2013-06-20 19:59:11

标签: string perl utf-8 character-encoding

以下内容:

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$str有什么不同,一个被认为是UTF-8而另一个不是?{
在任何情况下$str的编码是什么? ASCII?这是Perl的默认值吗?

3 个答案:

答案 0 :(得分: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

(在内部,UTF-8的扩展称为“utf8”用于存储72位字符串。除了意识到性能影响之外,这不是你应该知道的事情,但是有一些错误会暴露出来这个事实。)

编码is_utf8报告标量包含的字符串类型。除了调试我之前提到的错误之外,它是一个绝对没用的功能。

  • 8位字符串可以存储"abc"的值(或OP的$str中的字符串),因此Perl使用更有效的8位(UTF8 = 0)字符串格式。 / LI>
  • 8位字符串无法存储"\x{2660}\x{2661}"的值(或OP的$string中的字符串),因此Perl使用72位(UTF8 = 1)字符串格式。 / LI>

零是零,无论它是存储在浮点数,有符号整数还是无符号整数中。类似地,字符串的存储格式不传达有关字符串值的信息。

  • 您可以将代码点存储为8位字符串(如果它们足够小),就像72位字符串一样容易。
  • 您可以将字节存储在72位字符串中,就像8位字符串一样容易。

事实上,Perl会随意在两种格式之间切换。例如,如果您将$string$str连接起来,您将获得72位格式的字符串。

如果您需要解决错误,可以使用内置utf8::downgradeutf8::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

答案 1 :(得分:5)

  

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

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

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

  

代码点通过编码方案编码为一系列八位字节。

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

  

但这是如何运作的?

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

当然,计算机不能,而内部数据结构必须具有某种形式的内部编码。由于"Perl can't keep a secret",细节偶尔会泄漏,所以会产生太多的混淆。

  

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

不,内部编码是松散的UTF8(没有破折号)。它没有UTF-8(a.k.a. UTF-8-strict)的一些限制。

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

    编码:: is_utf8

    ...是一个内部函数,是clearly marked as such。你作为程序员不应该偷看。但既然你想要偷看,没有人可以阻止你。 Devel::Peek::Dump是进入内部的更好工具。

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

答案 2 :(得分:3)

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