查看源代码 Unicode 语法
Elixir 在整个语言中都支持 Unicode。本文档是 Elixir 如何在其语法中支持 Unicode 的完整参考。
字符串("olá"
)和字符列表('olá'
)从 Elixir v1.0 开始支持 Unicode。字符串使用 UTF-8 编码。字符列表是 Unicode 代码点的列表。在这种情况下,内容将按开发人员编写的方式保留,不会进行任何转换。
从 Elixir v1.5 开始,Elixir 还支持变量、原子和调用中的 Unicode。本文档的重点是介绍 Elixir 如何在其语法中允许使用 Unicode 的高级概述。我们还提供了技术文档,描述 Elixir 如何符合 Unicode 规范。
要检查当前 Elixir 安装的 Unicode 版本,请运行 String.Unicode.version()
。
简介
Elixir 允许在其变量、原子和调用中使用 Unicode 字符。但是,Unicode 字符必须仍然遵守语言语法的规则。特别是,变量和调用不能以大写字母开头。从现在开始,我们将这些术语称为标识符。
标识符中允许的字符是 Unicode 规定的字符。一般来说,它仅限于当前仍在使用的语言书写系统通常使用的字符。特别是,它排除了诸如表情符号、替代数字表示法、音符等符号。
Elixir 对标识符施加了许多限制,以确保安全性。例如,单词“josé”可以用两种方式用 Unicode 写:作为字符 j o s é
的组合,以及作为字符 j o s e ́
的组合,其中重音符号是其自身的字符。前者称为 NFC 形式,后者为 NFD 形式。Elixir 将所有字符标准化为 NFC 形式。
Elixir 还禁止大多数情况下使用混合脚本。例如,无法将变量命名为 аdmin
,其中 а
为西里尔字母,其余字符为拉丁字母。这样做会引发以下错误
** (SyntaxError) invalid mixed-script identifier found: аdmin
Mixed-script identifiers are not supported for security reasons. 'аdmin' is made of the following scripts:
\u0430 а {Cyrillic}
\u0064 d {Latin}
\u006D m {Latin}
\u0069 i {Latin}
\u006E n {Latin}
Make sure all characters in the identifier resolve to a single script or a highly
restrictive script. See https://hexdocs.erlang.ac.cn/elixir/unicode-syntax.html for more information.
该字符必须全部为西里尔字母或全部为拉丁字母。根据高度限制性 Unicode 建议,Elixir 允许的唯一混合脚本是
- 拉丁语和汉语带注音
- 拉丁语和日语
- 拉丁语和韩语
最后,Elixir 还会在同一文件中对可混淆的标识符发出警告。例如,如果您在代码中同时使用变量 а
(西里尔字母)和 а
(拉丁字母),Elixir 会发出警告。
这就是 Unicode 如何在 Elixir 标识符中使用的整体介绍。简而言之,它的目标是支持当今使用的不同书写系统,同时保持 Elixir 语言本身清晰安全。
有关技术细节,请参阅下一部分,其中涵盖了技术 Unicode 要求。
Unicode 附录 #31
Elixir 实现了 Unicode 附录 #31 版本 15.0 中概述的要求。
R1. 默认标识符
一般 Elixir 标识符规则指定为
<Identifier> := <Start> <Continue>* <Ending>?
其中 <Start>
使用与规范相同的类别,但将其标准化为 NFC 形式(见 R4)
从 Unicode 通用类别的大写字母、小写字母、首字母大写字母、修饰符字母、其他字母、字母数字派生的字符,以及
Other_ID_Start
,减去Pattern_Syntax
和Pattern_White_Space
代码点。在集合符号中:
[\p{L}\p{Nl}\p{Other_ID_Start}-\p{Pattern_Syntax}-\p{Pattern_White_Space}]
。
以及 <Continue>
使用与规范相同的类别,但将其标准化为 NFC 形式(见 R4)
ID_Start 字符,加上具有 Unicode 通用类别非间隔标记、间隔组合标记、十进制数字、连接标点符号的字符,以及
Other_ID_Continue
,减去Pattern_Syntax
和Pattern_White_Space
代码点。在集合符号中:
[\p{ID_Start}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Other_ID_Continue}-\p{Pattern_Syntax}-\p{Pattern_White_Space}]
。
<Ending>
是 Elixir 特有的补充,它仅包含代码点 ?
(003F) 和 !
(0021)。
该规范还提供了一个 <Medial>
集合,但 Elixir 不包括该集合中的任何字符。因此,标识符规则已简化为考虑这一点。
Elixir 不允许在标识符中使用 ZWJ 或 ZWNJ,因此不实现 R1a。双向控制字符也不受支持。出于向后兼容性目的,保证 R1b。
原子
Elixir 中的 Unicode 原子遵循上述标识符规则,并进行以下修改
<Start>
还包括代码点_
(005F)<Continue>
还包括代码点@
(0040)
请注意,原子也可以用引号括起来,这允许使用任何字符,例如 :"hello elixir"
。所有 Elixir 运算符也是有效的原子,例如 :+
、:@
、:|>
等。有效原子的完整描述在 "语法参考中的“原子”部分" 中提供。
变量、本地调用和远程调用
Elixir 中的变量遵循上述标识符规则,并进行以下修改
<Start>
还包括代码点_
(005F)<Start>
还排除 Lu(大写字母)和 Lt(首字母大写字母)字符
在集合符号中:[\u{005F}\p{Ll}\p{Lm}\p{Lo}\p{Nl}\p{Other_ID_Start}-\p{Pattern_Syntax}-\p{Pattern_White_Space}]
。
别名
Elixir 中的别名仅允许 ASCII 字符,以大写字母开头,并且没有标点符号。
R3. Pattern_White_Space 和 Pattern_Syntax 字符
Elixir 仅支持代码点 \t
(0009)、\n
(000A)、\r
(000D) 和 \s
(0020) 作为空格,因此不遵循要求 R3。R3 要求支持更多种类的空格和语法字符。
R4. 等效标准化标识符
Elixir 中的标识符区分大小写。
Elixir 将所有原子和变量标准化为 NFC 形式。但是,引号括起来的原子和字符串可以采用任何形式,并且不会被解析器验证。
换句话说,原子 :josé
只能用代码点 006A 006F 0073 00E9
或 006A 006F 0073 0065 0301
编写,但 Elixir 会将其改写为前者(从 Elixir 1.14 开始)。另一方面,:"josé"
可以写成 006A 006F 0073 00E9
或 006A 006F 0073 0065 0301
,并且其形式将被保留,因为它是在引号之间编写的。
选择要求 R4 会自动排除要求 R5、R6 和 R7。
Unicode 技术标准 #39
Elixir 符合 Unicode 技术标准 #39 版本 15.0 中概述的条款,该标准关于安全性。
C1. 标识符的一般安全配置文件
Elixir 将不允许使用 \p{Identifier_Status=Restricted}
中的代码点对标识符进行标记。
遵循一般安全配置文件的实现不允许
\p{Identifier_Status=Restricted}
中的任何字符,...
例如,'HANGUL FILLER' (ㅤ
) 字符通常是不可见的,它是一个不常见的代码点,会触发此警告。
请参阅下面有关其他规范化的说明,这些规范化可以对某些受限制的标识符进行自动替换。
C2. 可混淆检测
Elixir 将对看起来相同但不同的标识符发出警告。例如:在 а = a = 1
中,两个“a”字符分别是西里尔字母和拉丁字母,可能会相互混淆;在 力 = カ = 1
中,两者都是日语,但代码点不同,属于该书写系统的不同脚本。可混淆的标识符会导致难以发现的错误(例如,由于复制粘贴的代码),并且可能不安全,因此我们将对同一个文件中可能相互混淆的标识符发出警告。
我们使用第 4 节“可混淆检测”中描述的方法,并进行了一项修改
或者,它应声明使用修改,并提供一个精确的字符映射列表,这些列表是在提供的字符映射列表中添加或删除的。
Elixir 不会对仅由 a-z、A-Z、0-9 和 _ 组成的标识符的可混淆性发出警告。这是因为 ASCII 标识符已经存在了很长时间,程序员社区已经有自己的方法来处理诸如 l,1
或 O,0
之类的标识符之间的可混淆性(例如,专为编程设计的字体通常可以轻松区分这些字符)。
C3. 混合脚本检测
Elixir 将不允许对混合脚本标识符进行标记,除非混合是 UTS 39 5.2 “高度限制性”中定义的例外之一。我们使用第 5.1 节“混合脚本检测”中描述的方法来确定是否正在发生脚本混合,并对“其他规范化”部分中记录的修改进行修改。
例如:Elixir 允许像 幻ㄒㄧㄤ
这样的标识符,即使它包含来自多个“脚本”的字符,因为当应用 UTS 39 5.1 中的解析规则时,这些脚本都“解析”为日语。它还允许像 :Tシャツ
这样的原子,它是日语中的“T 恤”,它包含一个拉丁大写 T,因为 {Latn, Jpan} 是 UTS 39 5.2 中“高度限制性”定义中允许的脚本混合之一,并且它“涵盖”了该字符串。
但是,Elixir 将阻止对类似 if аdmin, do: :ok, else: :err
的代码进行标记,其中“a”字符的脚本集为 {Cyrillic},而所有其他字符的脚本集为 {Latin}。脚本集无法解析,并且 UTS 39 5.2 中“高度限制性”定义中的脚本集也无法涵盖该字符串,因此将显示描述性错误。
C4、C5(不适用)
不主张“C4 - 限制级别检测”的一致性,并且它不适用于代码中的标识符;相反,它适用于将给定任意字符串的安全性级别分类为 5 个限制级别中的一个。
“C5 - 混合数字检测”的一致性不适用,因为 Elixir 不支持 Unicode 数字。
添加规范化和已记录的 UTS 39 修改
从 Elixir 1.14 开始,\p{Identifier_Status=Restricted}
中的一些代码点将标准化为其他不受限制的代码点。
最初,这只是为了将 MICRO SIGN µ
转换为希腊小写 mu,μ
。
这不是对 UTS39 条款 C1(通用安全配置文件)或 C2(混淆检测)的修改;然而,它是对 C3,“混合脚本检测”的已记录修改。
混合脚本检测通过这些归一化进行修改,归一化的码点被赋予来自两个字符的脚本集的并集。
例如,在 MICRO => MU 的示例中,Micro 是一个“Common”脚本字符 - 与“_”下划线码点相同的脚本 - 因此归一化字符的脚本集将是 {Greek, Common}。'Common' 与所有非空脚本集相交,因此归一化字符可以在任何脚本编写的令牌中使用,而不会导致脚本混合。
以这种方式归一化的码点是那些在社区中使用的,并且被判断不太可能导致不安全的脚本混合问题。例如,MICRO 或 MU 码点可以用于处理微秒的原子或变量。