檢視原始碼 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.dev.org.tw/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} 中有任何字元,...
例如,經常看不見的「韓文字元填塞」(ㅤ
) 字元,是一個不常見的碼點,會觸發此警告。
請參閱下方關於其他正規化的注意事項,這些正規化可以自動替換一些受限制的識別碼。
C2. 混淆偵測
Elixir 會對看起來相同但實際上並非如此的識別碼發出警告。範例:在 а = a = 1
中,兩個「a」字元分別是西里爾字母和拉丁字母,可能會彼此混淆;在 力 = カ = 1
中,兩個字元都是日文,但不同的碼點,屬於該書寫系統的不同文字。混淆的識別碼可能導致難以發現的錯誤(例如,由於複製貼上的程式碼),而且可能不安全,因此我們會對單一檔案中可能彼此混淆的識別碼發出警告。
我們使用第 4 節「混淆偵測」中所述的方法,並做了一個修改
或者,它應宣告使用修改,並提供已新增或已移除的字元對應清單。
對於僅由 a-z、A-Z、0-9 和 _ 組成的識別碼,Elixir 不會對混淆性發出警告。這是因為 ASCII 識別碼已經存在很長一段時間,因此程式設計社群已經有自己的方法來處理 l,1
或 O,0
等識別碼之間的混淆性(例如,專為程式設計設計的字型通常可以輕易區分這些字元)。
C3. 混合文字偵測
除非混合是 UTS 39 5.2 中定義的例外之一「高度限制」,否則 Elixir 不允許對混合腳本識別碼進行標記化。我們使用第 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}
中的一些碼點會正規化為其他未限制的碼點。
最初,這只會用於將微標誌 µ
轉換為希臘小寫 mu,μ
。
這並未修改 UTS39 條款 C1(一般安全性設定檔)或 C2(混淆偵測);然而,它是 C3「混合腳本偵測」的記載修改。
混合腳本偵測會因這些正規化而修改,正規化碼點會從兩個字元取得腳本集的聯集。
例如,在 MICRO => MU 的範例中,Micro 是「一般」腳本字元,與底線碼點「_」給予的腳本相同,因此正規化字元的腳本集將為 {希臘文、一般}。「一般」與所有非空腳本集相交,因此正規化字元可用於以任何腳本編寫的標記,而不會造成腳本混合。
以這種方式正規化的碼點是社群中使用的碼點,而且判斷不太可能因不安全的腳本混合而造成問題。例如,MICRO 或 MU 碼點可用於處理微秒的原子或變數。