Raku 对 Unicode 具有高度支持,最新版本支持 Unicode 15.0。本文档旨在概述并描述不属于例程和方法文档的 Unicode 功能。
虽然它是 Rakudo
使用的 VM 的一部分,但这份关于 MoarVM 字符串内部表示的 概述 提供了一些有趣的细节。
出于安全原因,浏览器(例如:Firefox)可能会限制显示不在受信任字体中的 Unicode 字符,即使操作系统安装了显示该字符的字体。要解决 Firefox 的此问题,请将 `privacy.fingerprintingProtection` 配置选项设置为 `False`。
文件句柄和 I/O§
规范化§
Raku 默认情况下对所有输入和输出应用规范化,除了文件名,文件名以 UTF8-C8
格式读取和写入;字素(grapheme)是字符的用户可见形式,将使用规范化的表示形式。例如,字素 á
可以用两种方式表示,一种是用一个码点
á (U+E1 "LATIN SMALL LETTER A WITH ACUTE")
或者两个码点
a + ́ (U+61 "LATIN SMALL LETTER A" + U+301 "COMBINING ACUTE ACCENT")
Raku 会将这两个输入都转换为一个码点,如规范化形式 C (NFC) 所指定。在大多数情况下,这很有用,这意味着两个等效的输入都会被视为相同。Unicode 有一个规范等效的概念,它允许我们确定字符串的规范形式,从而允许我们正确地比较字符串并操作它们,而不必担心文本丢失这些属性。默认情况下,您从 Raku 处理或输出的任何文本都将以这种“规范”形式存在,即使对字符串进行修改或连接也是如此(有关如何避免这种情况,请参见下文)。有关规范化形式 C 和规范等效的更多详细信息,请参阅 Unicode 基金会关于 规范化和规范等效 的页面。
我们不默认使用规范化的一种情况是文件名。这是因为文件名必须完全按照磁盘上写入的字节进行访问。
要避免规范化,您可以使用一种称为 UTF8-C8 的特殊编码格式。在任何文件句柄上使用此编码将允许您读取磁盘上的确切字节,而不会进行规范化。如果您使用 UTF8 句柄打印它们,它们看起来可能很奇怪。如果您将其打印到输出编码为 UTF8-C8 的句柄,那么它将按预期呈现,因为它是逐字节的精确副本。有关 MoarVM 上 UTF8-C8 的更多技术细节,请参见下文。
UTF8-C8§
UTF-8 Clean-8 是一种编码器/解码器,它主要作为 UTF-8 编码器/解码器工作。但是,在遇到无法解码为有效 UTF-8 的字节序列,或者由于规范化而无法进行往返的字节序列时,它将使用 NFG 合成 来跟踪所涉及的原始字节。这意味着编码回 UTF-8 Clean-8 将能够重新创建字节,就像它们最初存在一样。合成包含四个码点
码点 0x10FFFD(这是一个私有使用码点)
码点 'x'
不可解码字节的高 4 位作为十六进制字符 (0..9A..F)
低 4 位作为不可解码字节的十六进制字符 (0..9A..F)
在正常的 UTF-8 编码下,这意味着不可表示的字符将显示为类似 ?xFF
的内容。
UTF-8 Clean-8 用于 MoarVM 从环境、命令行参数和文件系统查询接收字符串的地方;例如,在解码缓冲区时
say Buf.new(ord('A'), 0xFE, ord('Z')).decode('utf8-c8');# OUTPUT: «AxFEZ»
您可以看到 UTF8-C8 使用的两个初始码点在 'FE' 之前显示。您可以使用这种类型的编码来读取编码未知的文件
my = "/tmp/test";given open(, :w, :bin)say slurp(, enc => 'utf8-c8');# OUTPUT: «(65 250 66 251 252 67 253)»
使用这种类型的编码读取它们并将其编码回 UTF8-C8 将为您提供原始字节;使用默认的 UTF-8 编码将无法实现这一点。
请注意,这种编码目前在 Rakudo 的 JVM 实现中不受支持。
输入 Unicode 码点和码点序列§
您可以通过数字(十进制和十六进制)输入 Unicode 码点。例如,名为“带长音的拉丁大写字母 ae”的字符的十进制码点为 482,十六进制码点为 0x1E2
say "\c[482]"; # OUTPUT: «Ǣ»say "\x1E2"; # OUTPUT: «Ǣ»
您也可以通过名称访问 Unicode 码点:Raku 支持所有 Unicode 名称。
say "\c[PENGUIN]"; # OUTPUT: «🐧»say "\c[BELL]"; # OUTPUT: «🔔» (U+1F514 BELL)
所有 Unicode 码点名称/命名序列/表情符号序列现在不区分大小写:[从 Rakudo 2017.02 开始]
say "\c[latin capital letter ae with macron]"; # OUTPUT: «Ǣ»say "\c[latin capital letter E]"; # OUTPUT: «E» (U+0045)
您可以使用逗号分隔的列表以及 \c[]
来指定多个字符。您也可以将数字和命名样式结合起来
say "\c[482,PENGUIN]"; # OUTPUT: «Ǣ🐧»
除了在插值字符串中使用 \c[]
之外,您还可以使用 uniparse
say "DIGIT ONE".uniparse; # OUTPUT: «1»say uniparse("DIGIT ONE"); # OUTPUT: «1»
有关以相反方向(单个码点和多个码点)工作的例程,请参见 uniname 和 uninames。
名称别名§
名称别名主要用于没有官方名称的码点、缩写或更正(Unicode 名称永远不会改变)。有关它们的完整列表,请参见 此处。
没有官方名称的控制代码
say "\c[ALERT]"; # Not visible (U+0007 control code (also accessible as \a))say "\c[LINE FEED]"; # Not visible (U+000A same as "\n")
更正
# Correct name as input:say "\c[LATIN CAPITAL LETTER GHA]"; # OUTPUT: «Ƣ»# Original, erroneous name as output:say "Ƣ".uniname; # OUTPUT: «LATIN CAPITAL LETTER OI»# This one is a spelling mistake that was corrected in a Name Alias:# Correct name as input:say "\c[PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRACKET]".uniname;# Original, erroneous name as output:# OUTPUT: «PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET»
缩写
say "\c[ZWJ]".uniname; # OUTPUT: «ZERO WIDTH JOINER»say "\c[NBSP]".uniname; # OUTPUT: «NO-BREAK SPACE»say "\c[NNBSP]".uniname; # OUTPUT: «NARROW NO-BREAK SPACE»
命名序列§
您也可以使用任何 命名序列,这些不是单个码点,而是它们的序列。[从 Rakudo 2017.02 开始]
say "\c[LATIN CAPITAL LETTER E WITH VERTICAL LINE BELOW AND ACUTE]"; # OUTPUT: «É̩»say "\c[LATIN CAPITAL LETTER E WITH VERTICAL LINE BELOW AND ACUTE]".ords; # OUTPUT: «(201 809)»
表情符号序列§
Raku 支持表情符号序列。有关所有表情符号序列,请参见:表情符号 ZWJ 序列 和 表情符号序列。请注意,任何包含逗号的名称都应删除逗号,因为 Raku 使用逗号来分隔同一 \c
序列中的不同码点/序列。
say "\c[woman gesturing OK]"; # OUTPUT: «🙆♀️»say "\c[family: man woman girl boy]"; # OUTPUT: «👨👩👧👦»
混淆性§
由于 Unicode 中有大量的字形,更广泛的支持意味着您可能会发现一些容易混淆的字形。有关如何避免问题的通用提示,请参阅 unicode.org 网站上的 混淆性。