perluniintro - Perl Unicode 简介
本文档概述了 Unicode 及如何在 Perl 中使用 Unicode。有关 Unicode 的更深入介绍,请参阅 "更多资源"。
Unicode 是一种字符集标准,旨在对世界上所有书写系统以及许多其他符号进行编码。
Unicode 和 ISO/IEC 10646 是协调一致的标准,它们统一了几乎所有其他现代字符集标准,涵盖了 80 多种书写系统和数百种语言,包括所有商业上重要的现代语言。最大的中文、日文和韩文词典中的所有字符也都进行了编码。这些标准最终将涵盖 250 多种书写系统和数千种语言中的几乎所有字符。Unicode 1.0 于 1991 年 10 月发布,6.0 于 2010 年 10 月发布。
Unicode 字符是一个抽象实体。它不绑定到任何特定的整数宽度,特别是与 C 语言的 char
无关。Unicode 是语言中立的,也是显示中立的:它不编码文本的语言,并且通常不定义字体或其他图形布局细节。Unicode 对字符和由这些字符构建的文本进行操作。
Unicode 定义了诸如 LATIN CAPITAL LETTER A
或 GREEK SMALL LETTER ALPHA
之类的字符,以及这些字符的唯一编号,在本例中分别为 0x0041 和 0x03B1。这些唯一编号称为 码点。码点本质上是字符在所有可能的 Unicode 字符集中的位置,因此在 Perl 中,术语 序数 通常与它互换使用。
Unicode 标准更喜欢使用十六进制表示法来表示码点。如果您不熟悉 0x0041
之类的数字,请查看后面的部分,"十六进制表示法"。Unicode 标准使用 U+0041 LATIN CAPITAL LETTER A
表示法,以给出十六进制码点和字符的规范名称。
Unicode 还为字符定义了各种 属性,例如“大写”或“小写”、“十进制数字”或“标点符号”;这些属性独立于字符的名称。此外,还定义了对字符的各种操作,例如大写、小写和排序(排序)。
Unicode 逻辑“字符”实际上可以包含多个内部 实际“字符”或码点。对于西方语言,这可以通过一个 基字符(如 LATIN CAPITAL LETTER A
)后跟一个或多个 修饰符(如 COMBINING ACUTE ACCENT
)来充分建模。这个基字符和修饰符的序列称为 组合字符序列。一些非西方语言需要更复杂的模型,因此 Unicode 创建了 音节簇 概念,后来进一步细化为 扩展音节簇。例如,一个韩语谚文音节被认为是一个逻辑字符,但通常由三个实际的 Unicode 字符组成:一个前导辅音后跟一个内部元音,最后是一个尾随辅音。
是否将这些扩展的字形簇称为“字符”取决于你的观点。如果你是一名程序员,你可能倾向于将序列中的每个元素视为一个单元,或“字符”。然而,从用户的角度来看,整个序列可以被视为一个“字符”,因为这可能是它在用户语言环境中的显示方式。在本文件中,我们采用程序员的观点:一个“字符”就是一个 Unicode 代码点。
对于某些基本字符和修饰符的组合,存在 *预组合* 字符。例如,对于序列 LATIN CAPITAL LETTER A
后跟 COMBINING ACUTE ACCENT
,存在一个单字符等效项。它被称为 LATIN CAPITAL LETTER A WITH ACUTE
。然而,这些预组合字符仅适用于某些组合,主要用于支持 Unicode 与传统标准(如 ISO 8859)之间的往返转换。Unicode 使用序列,允许使用更少的构建块(代码点)来表达更多潜在的字形簇。为了支持等效形式之间的转换,还定义了各种 *规范化形式*。因此,LATIN CAPITAL LETTER A WITH ACUTE
处于 *规范化形式组合*(缩写为 NFC)中,而序列 LATIN CAPITAL LETTER A
后跟 COMBINING ACUTE ACCENT
在 *规范化形式分解*(NFD)中表示相同的字符。
由于与传统编码的向后兼容性,"每个字符一个唯一数字" 的理念略有失效:取而代之的是 "每个字符至少一个数字"。同一个字符可以在几个传统编码中以不同的方式表示。反之则不成立:某些代码点没有分配字符。首先,在已使用的块中存在未分配的代码点。其次,存在一些特殊的 Unicode 控制字符,它们不代表真正的字符。
Unicode 最初设计时,人们认为可以用 16 位字来表示世界上所有的字符;也就是说,最多需要 0x10000
(或 65,536)个字符,从 0x0000
到 0xFFFF
。但这很快被证明是错误的,从 Unicode 2.0(1996 年 7 月)开始,Unicode 已被定义到 21 位(0x10FFFF
),而 Unicode 3.1(2001 年 3 月)定义了第一个超过 0xFFFF
的字符。前 0x10000
个字符被称为 *平面 0* 或 *基本多语言平面*(BMP)。随着 Unicode 3.1 的发布,总共定义了 17 个(是的,十七个)平面——但它们离完全充满定义的字符还很远。
当编码一种新的语言时,Unicode 通常会为其字符选择一个连续的未分配代码点的块
。到目前为止,这些块中的代码点数目始终是 16 的倍数。块中目前不需要的额外代码点将保留未分配,以备将来扩展。但也有过这种情况,即在后续版本中需要比可用额外代码点更多的代码点,而必须在其他地方分配一个新的块,而不是与初始块相邻,以处理溢出。因此,很早就发现“块”不是一个合适的组织原则,于是创建了Script
属性。(后来还添加了一个改进的脚本属性,即Script_Extensions
属性。)那些位于溢出块中的代码点仍然可以与原始代码点具有相同的脚本。脚本概念更符合自然语言:有Latin
脚本、Greek
脚本等等;还有几种人工脚本,例如Common
,用于在多个脚本中使用的字符,例如数学符号。脚本通常跨越多个块的不同部分。有关脚本的更多信息,请参阅 "Scripts" in perlunicode。块的划分是存在的,但几乎完全是偶然的——这是字符分配方式的产物。(请注意,本段为了介绍简化了内容。Unicode 实际上并不编码语言,而是编码语言的书写系统——它们的脚本;一个脚本可以被多种语言使用。Unicode 还编码了与语言无关的内容,例如BAGGAGE CLAIM
等符号。)
Unicode 代码点只是抽象的数字。为了输入和输出这些抽象数字,必须以某种方式对这些数字进行编码或序列化。Unicode 定义了几种字符编码形式,其中UTF-8 最受欢迎。UTF-8 是一种可变长度编码,它将 Unicode 字符编码为 1 到 4 个字节。其他编码包括 UTF-16 和 UTF-32 以及它们的大端和小端变体(UTF-8 与字节序无关)。ISO/IEC 10646 定义了 UCS-2 和 UCS-4 编码形式。
有关编码的更多信息——例如,要了解什么是代理和字节顺序标记 (BOM)——请参阅 perlunicode。
从 Perl v5.6.0 开始,Perl 就具备了原生处理 Unicode 的能力。然而,Perl v5.8.0 是第一个推荐用于严肃 Unicode 工作的版本。维护版本 5.6.1 修复了最初 Unicode 实现中的许多问题,但例如正则表达式在 5.6.1 中仍然无法使用 Unicode。Perl v5.14.0 是第一个 Unicode 支持(几乎)无缝集成且没有一些问题的版本。(有一些例外。首先,从 Perl 5.16.0 开始修复了 quotemeta 中的一些差异。其次,从 Perl 5.26.0 开始修复了 范围运算符 中的一些差异。第三,从 Perl 5.28.0 开始修复了 split 中的一些差异。)
为了启用这种无缝支持,您应该使用 use feature 'unicode_strings'
(如果您使用 use v5.12
或更高版本,则会自动选择)。请参阅 feature。(5.14 还修复了许多错误和与 Unicode 标准的偏差。)
在 Perl v5.8.0 之前,使用 use utf8
来声明当前块或文件中的操作将是 Unicode 意识的。这种模型被发现是错误的,或者至少是笨拙的:“Unicode 性”现在与数据一起携带,而不是附加到操作。从 Perl v5.8.0 开始,只有一种情况下仍然需要显式 use utf8
:如果您的 Perl 脚本本身以 UTF-8 编码,您可以在标识符名称、字符串和正则表达式字面量中使用 UTF-8,方法是说 use utf8
。这不是默认设置,因为包含旧版 8 位数据的脚本会中断。请参阅 utf8。
Perl 支持两种字符串:5.6 之前的八位字节原生字符串和 Unicode 字符串。一般原则是,Perl 尽可能地将数据保留为八位字节,但一旦不可避免地需要使用 Unicode,数据就会透明地升级为 Unicode。在 Perl v5.14.0 之前,升级并非完全透明(参见 "The "Unicode Bug"" in perlunicode),为了向后兼容,除非选择 use feature 'unicode_strings'
(参见 feature)或 use v5.12
(或更高版本),否则无法获得完全透明性。
在内部,Perl 目前使用平台的原生八位字符集(例如 Latin-1)或 UTF-8 来编码 Unicode 字符串,默认使用 UTF-8。具体来说,如果字符串中的所有代码点都小于或等于 0xFF
,Perl 将使用原生八位字符集。否则,它将使用 UTF-8。
Perl 用户通常不需要知道或关心 Perl 如何编码其内部字符串,但这在将 Unicode 字符串输出到没有 PerlIO 层(使用“默认”编码的层)的流时变得很重要。在这种情况下,将使用内部使用的原始字节(原生字符集或 UTF-8,根据每个字符串的需要),如果这些字符串包含超过 0x00FF 的字符,则会发出“宽字符”警告。
例如,
perl -e 'print "\x{DF}\n", "\x{0100}\x{DF}\n"'
会生成原生字节和 UTF-8 的混合,并且会发出警告
Wide character in print at ...
要输出 UTF-8,请使用 :encoding
或 :utf8
输出层。在示例程序前面添加
binmode(STDOUT, ":utf8");
可以确保输出完全是 UTF-8,并消除程序的警告。
可以使用 -C
命令行开关或 PERL_UNICODE
环境变量来启用标准文件句柄、默认 open()
层和 @ARGV
的自动 UTF-8 化,有关 -C
开关的文档,请参见 perlrun。
请注意,这意味着 Perl 预计其他软件以相同的方式工作:如果 Perl 被告知 STDIN 应该是 UTF-8,但来自另一个命令的 STDIN 不是 UTF-8,Perl 可能会抱怨 UTF-8 格式错误。
所有将 Unicode 和 I/O 结合在一起的功能都需要使用新的 PerlIO 功能。几乎所有 Perl 5.8 平台都使用 PerlIO,您可以通过运行“perl -V”并查找 useperlio=define
来查看您的平台是否使用 PerlIO。
Perl 5.8.0 在 EBCDIC 平台上添加了对 Unicode 的支持。这种支持在后来的版本中被允许失效,但在 5.22 中被恢复。Unicode 支持的实现稍微复杂一些,因为需要额外的转换。有关更多信息,请参阅 perlebcdic。
在 EBCDIC 平台上,内部 Unicode 编码形式是 UTF-EBCDIC 而不是 UTF-8。区别在于 UTF-8 是“ASCII 安全的”,因为 ASCII 字符按原样编码为 UTF-8,而 UTF-EBCDIC 是“EBCDIC 安全的”,因为所有基本字符(包括所有具有 ASCII 等效字符的字符(如 "A"
、"0"
、"%"
等)在 EBCDIC 和 UTF-EBCDIC 中都是相同的。通常,文档会使用“UTF-8”一词来表示 UTF-EBCDIC。本文件也是如此。
本节完全适用于从 v5.22 开始的 Perl。早期版本的各种注意事项在下面的 "早期版本注意事项" 小节中。
要在文字中创建 Unicode 字符,请在双引号字符串中使用 \N{...}
符号
my $smiley_from_name = "\N{WHITE SMILING FACE}";
my $smiley_from_code_point = "\N{U+263a}";
类似地,它们也可以在正则表达式文字中使用
$smiley =~ /\N{WHITE SMILING FACE}/;
$smiley =~ /\N{U+263a}/;
或者,从 v5.32 开始
$smiley =~ /\p{Name=WHITE SMILING FACE}/;
$smiley =~ /\p{Name=whitesmilingface}/;
在运行时,您可以使用
use charnames ();
my $hebrew_alef_from_name
= charnames::string_vianame("HEBREW LETTER ALEF");
my $hebrew_alef_from_code_point = charnames::string_vianame("U+05D0");
当然,ord()
会执行相反的操作:它将字符转换为代码点。
还有其他运行时选项。您可以使用 pack()
my $hebrew_alef_from_code_point = pack("U", 0x05d0);
或者您可以使用 chr()
,尽管在一般情况下它不太方便
$hebrew_alef_from_code_point = chr(utf8::unicode_to_native(0x05d0));
utf8::upgrade($hebrew_alef_from_code_point);
如果参数大于 0xFF,则不需要 utf8::unicode_to_native()
和 utf8::upgrade()
,因此上面的代码可以写成
$hebrew_alef_from_code_point = chr(0x05d0);
因为 0x5d0 大于 255。
\x{}
和 \o{}
也可以在双引号字符串中用于在编译时指定代码点,但为了与旧版本的 Perl 保持向后兼容性,对于小于 256 的代码点,将应用与 chr()
相同的规则。
utf8::unicode_to_native()
用于使 Perl 代码可移植到 EBCDIC 平台。如果您真的确定没有人会想在非 ASCII 平台上使用您的代码,则可以省略它。从 Perl v5.22 开始,在 ASCII 平台上对它的调用被优化掉了,因此添加它不会有任何性能损失。或者,您也可以简单地使用不需要它的其他结构。
有关如何查找所有这些名称和数字代码,请参阅 "更多资源"。
在 EBCDIC 平台上,在 v5.22 之前,使用 \N{U+...}
无法正常工作。
在 v5.16 之前,使用 \N{...}
带字符名称(而不是 U+...
代码点)需要一个 use charnames :full
。
在 v5.14 之前,\N{...}
带字符名称(而不是 U+...
代码点)存在一些错误。
charnames::string_vianame()
在 v5.14 中引入。在此之前,charnames::vianame()
应该可以工作,但前提是参数必须是 "U+..."
形式。在运行时通过字符名称处理 Unicode 的最佳方法可能是
use charnames ();
my $hebrew_alef_from_name
= pack("U", charnames::vianame("HEBREW LETTER ALEF"));
在大多数情况下,处理 Unicode 是透明的:只需像往常一样使用字符串即可。index()
、length()
和 substr()
等函数将在 Unicode 字符上工作;正则表达式将在 Unicode 字符上工作(参见 perlunicode 和 perlretut)。
请注意,Perl 将音节群视为单独的字符,因此例如
print length("\N{LATIN CAPITAL LETTER A}\N{COMBINING ACUTE ACCENT}"),
"\n";
将打印 2,而不是 1。唯一的例外是正则表达式有 \X
用于匹配扩展音节群。(因此,正则表达式中的 \X
将匹配这两个示例字符的整个序列。)
然而,在处理遗留编码、I/O 和某些特殊情况时,情况并非如此透明
当您将遗留数据和 Unicode 结合在一起时,需要将遗留数据升级到 Unicode。通常,遗留数据被假定为 ISO 8859-1(或 EBCDIC,如果适用)。
Encode
模块了解许多编码,并提供用于在这些编码之间进行转换的接口
use Encode 'decode';
$data = decode("iso-8859-3", $data); # convert from legacy
通常,写入 Unicode 数据
print FH $some_string_with_unicode, "\n";
会生成 Perl 用于内部编码 Unicode 字符串的原始字节。Perl 的内部编码取决于系统以及字符串中当时存在的字符。如果任何字符的代码点为 0x100
或更高,您将收到警告。为了确保输出以您想要的编码显式呈现,并避免警告,请使用所需的编码打开流。以下是一些示例
open FH, ">:utf8", "file";
open FH, ">:encoding(ucs2)", "file";
open FH, ">:encoding(UTF-8)", "file";
open FH, ">:encoding(shift_jis)", "file";
以及在已打开的流上,使用 binmode()
binmode(STDOUT, ":utf8");
binmode(STDOUT, ":encoding(ucs2)");
binmode(STDOUT, ":encoding(UTF-8)");
binmode(STDOUT, ":encoding(shift_jis)");
编码名称的匹配比较宽松:不区分大小写,并且许多编码都有多个别名。请注意,:utf8
层必须始终按原样指定;它不受编码名称宽松匹配的影响。另请注意,目前 :utf8
对输入是不安全的,因为它接受数据而不验证其是否确实是有效的 UTF-8;您应该改为使用 :encoding(UTF-8)
(带或不带连字符)。
有关 :utf8
层,请参阅 PerlIO;有关 :encoding()
层,请参阅 PerlIO::encoding 和 Encode::PerlIO;有关 Encode
模块支持的许多编码,请参阅 Encode::Supported。
读取您知道以某种 Unicode 或传统编码编码的文件不会神奇地将数据在 Perl 的眼中转换为 Unicode。为此,请在打开文件时指定适当的层。
open(my $fh,'<:encoding(UTF-8)', 'anything');
my $line_of_unicode = <$fh>;
open(my $fh,'<:encoding(Big5)', 'anything');
my $line_of_unicode = <$fh>;
I/O 层也可以使用 open
编译指示更灵活地指定。请参阅 open,或查看以下示例。
use open ':encoding(UTF-8)'; # input/output default encoding will be
# UTF-8
open X, ">file";
print X chr(0x100), "\n";
close X;
open Y, "<file";
printf "%#x\n", ord(<Y>); # this should print 0x100
close Y;
使用 open
编译指示,您可以使用 :locale
层。
BEGIN { $ENV{LC_ALL} = $ENV{LANG} = 'ru_RU.KOI8-R' }
# the :locale will probe the locale environment variables like
# LC_ALL
use open OUT => ':locale'; # russki parusski
open(O, ">koi8");
print O chr(0x430); # Unicode CYRILLIC SMALL LETTER A = KOI8-R 0xc1
close O;
open(I, "<koi8");
printf "%#x\n", ord(<I>), "\n"; # this should print 0xc1
close I;
这些方法在 I/O 流上安装一个透明过滤器,该过滤器在从流中读取数据时将其从指定编码转换为 Unicode。结果始终是 Unicode。
open
编译指示通过设置默认层来影响编译指示之后的所有 open()
调用。如果您只想影响某些流,请在 open()
调用中直接使用显式层。
您可以使用 binmode()
在已打开的流上切换编码;请参阅 "binmode" in perlfunc。
:locale
目前不适用于 open()
和 binmode()
,仅适用于 open
编译指示。:utf8
和 :encoding(...)
方法适用于 open()
、binmode()
和 open
编译指示。
类似地,您可以在输出流上使用这些 I/O 层,以便在将 Unicode 写入流时自动将其转换为指定的编码。例如,以下代码段将文件 "text.jis"(以 ISO-2022-JP 编码,也称为 JIS)的内容复制到文件 "text.utf8",以 UTF-8 编码。
open(my $nihongo, '<:encoding(iso-2022-jp)', 'text.jis');
open(my $unicode, '>:utf8', 'text.utf8');
while (<$nihongo>) { print $unicode $_ }
编码的命名,无论是通过 open()
还是通过 open
编译指示,都允许使用灵活的名称:koi8-r
和 KOI8R
都将被理解。
ISO、MIME、IANA 和各种其他标准化组织认可的常用编码都得到认可;有关更详细的列表,请参阅 Encode::Supported。
read()
读取字符并返回字符数。seek()
和 tell()
操作字节计数,sysseek()
也是如此。
sysread()
和 syswrite()
不应在具有字符编码层的句柄上使用,它们的行为很糟糕,这种行为从 perl 5.24 开始就被弃用。
请注意,由于默认行为是在输入时不进行任何转换(如果不存在默认层),因此很容易错误地编写代码,该代码通过重复编码数据来不断扩展文件。
# BAD CODE WARNING
open F, "file";
local $/; ## read in the whole file of 8-bit characters
$t = <F>;
close F;
open F, ">:encoding(UTF-8)", "file";
print F $t; ## convert to UTF-8 on output
close F;
如果您运行此代码两次,文件的内容将被两次 UTF-8 编码。使用 use open ':encoding(UTF-8)'
可以避免此错误,或者显式地将文件也以 UTF-8 格式打开以进行输入。
注意::utf8
和 :encoding
功能仅在您的 Perl 使用 PerlIO 构建时才有效,这是大多数系统上的默认设置。
有时您可能希望将包含 Unicode 的 Perl 标量显示为简单的 ASCII(或 EBCDIC)文本。以下子例程将转换其参数,以便代码点大于 255 的 Unicode 字符显示为 \x{...}
,控制字符(如 \n
)显示为 \x..
,其余字符则显示为自身
sub nice_string {
join("",
map { $_ > 255 # if wide character...
? sprintf("\\x{%04X}", $_) # \x{...}
: chr($_) =~ /[[:cntrl:]]/ # else if control character...
? sprintf("\\x%02X", $_) # \x..
: quotemeta(chr($_)) # else quoted or as themselves
} unpack("W*", $_[0])); # unpack Unicode characters
}
例如,
nice_string("foo\x{100}bar\n")
返回字符串
'foo\x{0100}bar\x0A'
该字符串已准备好打印。
(此处使用 \\x{}
而不是 \\N{}
,因为您最有可能希望看到本机值。)
从 Perl 5.28 开始,位运算符(如 ~
)对包含代码点大于 255 的字符串进行操作是非法的。
如果在包含字符的字符串上使用 vec()
函数,其结果可能出乎意料,这些字符的序数值大于 255。在这种情况下,结果与字符的内部编码一致,但与其他内容不一致。因此,请勿这样做,从 Perl 5.28 开始,如果您这样做,将发出弃用消息,在 Perl 5.32 中将变为非法。
窥视 Perl 的内部编码
Perl 的普通用户永远不应该关心 Perl 如何编码任何特定的 Unicode 字符串(因为通过明确定义的 I/O 层访问包含 Unicode 的字符串内容的正常方式始终是通过输入和输出)。但是,如果您必须这样做,有两种方法可以查看幕后情况。
窥视 Unicode 字符内部编码的一种方法是使用 unpack("C*", ...
获取字符串编码的字节,或者使用 unpack("U0..", ...)
获取 UTF-8 编码的字节
# this prints c4 80 for the UTF-8 bytes 0xc4 0x80
print join(" ", unpack("U0(H2)*", pack("U", 0x100))), "\n";
另一种方法是使用 Devel::Peek 模块
perl -MDevel::Peek -e 'Dump(chr(0x100))'
这将显示 FLAGS 中的 UTF8
标志以及 PV
中的 UTF-8 字节和 Unicode 字符。另请参阅本文档后面的关于 utf8::is_utf8()
函数的讨论。
字符串等价性
在 Unicode 中,字符串等价性的问题变得有些复杂:你所说的“相等”是什么意思?
(LATIN CAPITAL LETTER A WITH ACUTE
等于 LATIN CAPITAL LETTER A
吗?)
简短的回答是,默认情况下,Perl 仅根据字符的代码点比较等价性 (eq
, ne
)。在上面的例子中,答案是否定的(因为 0x00C1 != 0x0041)。但有时,任何大写字母 A 都应该被认为是相等的,甚至任何大小写的 A 都应该被认为是相等的。
长答案是,你需要考虑字符规范化和大小写问题:请参阅 Unicode::Normalize、Unicode 技术报告 #15、Unicode 规范化形式 以及 Unicode 标准 中关于大小写映射的部分。
从 Perl 5.8.0 开始,实现了 大小写映射/特殊大小写 的“完全”大小写折叠,但 qr//i
中仍然存在一些错误,这些错误大部分在 5.14 中修复,并在 5.18 中基本完全修复。
字符串排序
人们喜欢看到他们的字符串被很好地排序——或者用 Unicode 的术语来说,被排序。但同样,你所说的排序是什么意思?
(LATIN CAPITAL LETTER A WITH ACUTE
在 LATIN CAPITAL LETTER A WITH GRAVE
之前还是之后?)
简短的回答是,默认情况下,Perl 仅根据字符的代码点比较字符串 (lt
, le
, cmp
, ge
, gt
)。在上面的例子中,答案是“之后”,因为 0x00C1
> 0x00C0
。
长答案是“这取决于”,在不知道(至少)语言上下文的情况下,无法给出好的答案。请参阅 Unicode::Collate 和 Unicode 排序算法 https://www.unicode.org/reports/tr10/
字符范围和类别
正则表达式方括号字符类中的字符范围(例如,/[a-z]/
)以及 tr///
(也称为 y///
)运算符,不会神奇地支持 Unicode。这意味着 [A-Za-z]
不会神奇地开始表示“所有字母”(即使对于 8 位字符,它也不表示“所有字母”;对于 8 位字符,如果你使用的是区域设置 (perllocale),请使用 /[[:alpha:]]/
;如果不是,请使用 8 位感知属性 \p{alpha}
)。
所有以\p
(及其反义词\P
)开头的属性实际上都是 Unicode 意识的字符类。它们有几十个,参见perluniprops。
从 v5.22 开始,您可以使用 Unicode 代码点作为正则表达式模式字符范围的端点,该范围将包含所有位于这些端点之间的 Unicode 代码点,包括端点本身。
qr/ [ \N{U+03} - \N{U+20} ] /xx
包括代码点\N{U+03}
、\N{U+04}
、...、\N{U+20}
。
这也适用于 Perl v5.24 开始的tr///
中的范围。
字符串到数字的转换
Unicode 除了熟悉的 0 到 9 之外,还定义了其他几个十进制和数字字符,例如阿拉伯数字和印度数字。Perl 不支持除 ASCII 0
到 9
(以及十六进制的 ASCII a
到 f
)以外的数字的字符串到数字转换。要从任何 Unicode 字符串获得安全的转换,请使用"Unicode::UCD 中的 num()"。
我的旧脚本会坏掉吗?
很可能不会。除非您以某种方式生成 Unicode 字符,否则旧的行为应该会保留。唯一可能开始生成 Unicode 的行为变化是旧的chr()
行为,其中提供超过 255 的参数会产生一个模 255 的字符。例如,chr(300)
等于 chr(45)
或 "-"(在 ASCII 中),现在它变成了 LATIN CAPITAL LETTER I WITH BREVE。
如何使我的脚本与 Unicode 一起使用?
应该只需要做很少的工作,因为在您生成 Unicode 数据之前,什么都不会改变。最重要的是将输入获取为 Unicode;为此,请参阅前面的 I/O 讨论。要获得完整的无缝 Unicode 支持,请在您的脚本中添加use feature 'unicode_strings'
(或use v5.12
或更高版本)。
如何知道我的字符串是否在 Unicode 中?
您不应该关心。但是,如果您使用的 Perl 版本低于 5.14.0,或者您没有指定use feature 'unicode_strings'
或use 5.012
(或更高版本),您可能需要关心,因为否则,范围 128 到 255 中的代码点的规则将根据它们所在的字符串是否在 Unicode 中而有所不同。(参见"perlunicode 中的当 Unicode 不发生时"。)
要确定字符串是否在 Unicode 中,请使用
print utf8::is_utf8($string) ? 1 : 0, "\n";
但请注意,这并不意味着字符串中的任何字符都必须是 UTF-8 编码的,或者任何字符的代码点都大于 0xFF(255)甚至 0x80(128),或者字符串中根本没有任何字符。is_utf8()
所做的只是返回附加到$string
的内部“utf8ness”标志的值。如果标志关闭,则标量中的字节被解释为单字节编码。如果标志打开,则标量中的字节被解释为字符的(可变长度、可能的多字节)UTF-8 编码代码点。添加到 UTF-8 编码字符串的字节会自动升级为 UTF-8。如果混合了非 UTF-8 和 UTF-8 标量(双引号插值、显式连接或 printf/sprintf 参数替换),则结果将被 UTF-8 编码,就像字节字符串的副本被升级为 UTF-8 一样:例如,
$a = "ab\x80c";
$b = "\x{100}";
print "$a = $b\n";
输出字符串将是 UTF-8 编码的ab\x80c = \x{100}\n
,但$a
将保持字节编码。
有时您可能真的需要知道字符串的字节长度而不是字符长度。为此,请使用bytes
编译指示和length()
函数
my $unicode = chr(0x100);
print length($unicode), "\n"; # will print 1
use bytes;
print length($unicode), "\n"; # will print 2
# (the 0xC4 0x80 of the UTF-8)
no bytes;
如何确定文件的编码方式?
您可以尝试使用 Encode::Guess,但它有一些限制。
如何检测特定编码中无效的数据?
使用 Encode
包尝试转换它。例如,
use Encode 'decode';
if (eval { decode('UTF-8', $string, Encode::FB_CROAK); 1 }) {
# $string is valid UTF-8
} else {
# $string is not valid UTF-8
}
或者使用 unpack
尝试解码它
use warnings;
@chars = unpack("C0U*", $string_of_bytes_that_I_think_is_utf8);
如果无效,将产生 Malformed UTF-8 character
警告。 "C0" 表示 "逐字符处理字符串"。如果没有它,unpack("U*", ...)
将在 U0
模式下工作(如果格式字符串以 U
开头,则为默认模式),它将返回构成目标字符串 UTF-8 编码的字节,这始终有效。
如何将二进制数据转换为特定编码,反之亦然?
这可能不像您想象的那么有用。通常,您不需要这样做。
从某种意义上说,您所问的没有多大意义:编码用于字符,而二进制数据不是 "字符",因此将 "数据" 转换为某种编码没有意义,除非您知道二进制数据以什么字符集和编码存储,在这种情况下,它就不是简单的二进制数据了,对吧?
如果您有一个原始字节序列,并且知道它应该通过特定编码进行解释,可以使用 Encode
use Encode 'from_to';
from_to($data, "iso-8859-1", "UTF-8"); # from latin-1 to UTF-8
对 from_to()
的调用更改了 $data
中的字节,但就 Perl 而言,字符串的本质没有发生任何实质性变化。在调用之前和之后,字符串 $data
都只包含一堆 8 位字节。就 Perl 而言,字符串的编码仍然是 "系统原生 8 位字节"。
您可以将此与虚构的 'Translate' 模块进行比较
use Translate;
my $phrase = "Yes";
Translate::from_to($phrase, 'english', 'deutsch');
## phrase now contains "Ja"
字符串的内容发生了变化,但字符串的本质没有改变。调用之后,Perl 并不比调用之前更了解字符串的内容表示肯定。
回到数据转换。如果您有(或想要)系统原生 8 位编码中的数据(例如 Latin-1、EBCDIC 等),可以使用 pack/unpack 将其转换为/从 Unicode 转换。
$native_string = pack("W*", unpack("U*", $Unicode_string));
$Unicode_string = pack("U*", unpack("W*", $native_string));
如果你有一串你知道是有效 UTF-8 的字节,但 Perl 还不知道,你也可以让 Perl 相信。
$Unicode = $bytes;
utf8::decode($Unicode);
或者
$Unicode = pack("U0a*", $bytes);
你可以用以下方法找到构成 UTF-8 序列的字节:
@bytes = unpack("C*", $Unicode_string)
你可以用以下方法创建格式良好的 Unicode:
$Unicode_string = pack("U*", 0xff, ...)
如何显示 Unicode?如何输入 Unicode?
参见 http://www.alanwood.net/unicode/ 和 http://www.cl.cam.ac.uk/~mgk25/unicode.html
Unicode 如何与传统区域设置配合使用?
如果你的区域设置是 UTF-8 区域设置,从 Perl v5.26 开始,Perl 在所有类别中都能很好地工作;在此之前,从 Perl v5.20 开始,它在所有类别中都能很好地工作,除了 LC_COLLATE
,它处理排序和 cmp
运算符。但请注意,标准的 Unicode::Collate
和 Unicode::Collate::Locale
模块提供了更强大的排序解决方案,并且可以在更早的版本中使用。
对于其他区域设置,从 Perl 5.16 开始,你可以指定
use locale ':not_characters';
让 Perl 与它们良好地配合使用。问题是,你必须自己将字符集从区域设置转换为/从 Unicode 转换。请参见上面的 "Unicode I/O",了解如何
use open ':locale';
实现这一点,但完整的细节在 "Unicode and UTF-8" in perllocale 中,包括如果你没有指定 :not_characters
会发生的陷阱。
Unicode 标准更喜欢使用十六进制表示法,因为这更清楚地显示了 Unicode 如何划分为 256 个字符的块。十六进制也比十进制更短。你也可以使用十进制表示法,但学习使用十六进制会让使用 Unicode 标准变得更容易。例如,U+HHHH
表示法使用十六进制。
0x
前缀表示十六进制数,数字是 0-9 以及 a-f(或 A-F,大小写无关)。每个十六进制数字代表四个位,或半个字节。print 0x..., "\n"
将以十进制显示十六进制数,而 printf "%x\n", $decimal
将以十六进制显示十进制数。如果你只有十六进制数的“十六进制数字”,你可以使用 hex()
函数。
print 0x0009, "\n"; # 9
print 0x000a, "\n"; # 10
print 0x000f, "\n"; # 15
print 0x0010, "\n"; # 16
print 0x0011, "\n"; # 17
print 0x0100, "\n"; # 256
print 0x0041, "\n"; # 65
printf "%x\n", 65; # 41
printf "%#x\n", 65; # 0x41
print hex("41"), "\n"; # 65
Unicode 联盟
Unicode 常见问题解答
Unicode 词汇表
Unicode 推荐阅读清单
Unicode 联盟提供了一份文章和书籍清单,其中一些对 Unicode 进行了更深入的探讨:http://unicode.org/resources/readinglist.html
Unicode 有用资源
HTML、字体、Web 浏览器和其他应用程序中的 Unicode 和多语言支持
Unix/Linux 的 UTF-8 和 Unicode 常见问题解答
传统字符集
您可以使用 Unicode::UCD
模块探索 Unicode 数据文件中的各种信息。
如果您无法将 Perl 升级到 5.8.0 或更高版本,您仍然可以通过使用来自 CPAN 的 Unicode::String
、Unicode::Map8
和 Unicode::Map
模块来进行一些 Unicode 处理。如果您安装了 GNU recode,您还可以使用 Perl 前端 Convert::Recode
进行字符转换。
以下是将 ISO 8859-1 (Latin-1) 字节快速转换为 UTF-8 字节以及反向转换的代码,即使在旧版 Perl 5 版本中也能正常工作。
# ISO 8859-1 to UTF-8
s/([\x80-\xFF])/chr(0xC0|ord($1)>>6).chr(0x80|ord($1)&0x3F)/eg;
# UTF-8 to ISO 8859-1
s/([\xC2\xC3])([\x80-\xBF])/chr(ord($1)<<6&0xC0|ord($2)&0x3F)/eg;
perlunitut、perlunicode、Encode、open、utf8、bytes、perlretut、perlrun、Unicode::Collate、Unicode::Normalize、Unicode::UCD
感谢 [email protected]、[email protected]、[email protected] 和 [email protected] 邮件列表的热心读者提供宝贵的反馈。
版权所有 2001-2011 Jarkko Hietaniemi <[email protected]>。现在由 Perl 5 维护者维护。
本文件可以在与 Perl 本身相同的条款下分发。