內容

名稱

perlunicode - Perl 中的 Unicode 支援

說明

如果您尚未閱讀本文檔,您應該先熟悉 perlunitutperluniintro

Unicode 旨在將世界上所有字元集的編碼統一到一個標準中。對於 Unicode 最初建立時存在的許多編碼標準,從每個標準轉換為 Unicode 基本上表示將常數新增到原始標準中的每個碼點,而轉換回來則只表示減去相同的常數。對於 ASCII 和 ISO-8859-1,常數為 0。對於 ISO-8859-5(西里爾字母),常數為 864;對於希伯來語(ISO-8859-8),為 1488;對於泰語(ISO-8859-11),為 3424;以此類推。這使得轉換變得容易,並促進了 Unicode 的採用。

而且它奏效了;現在,那些舊標準很少被使用了。大多數人都使用 Unicode。

Unicode 是一個全面的標準。它指定了 Perl 範圍之外的許多事項,例如如何顯示字元序列。如需了解 Unicode 所有面向的完整討論,請參閱 https://www.unicode.org

重要注意事項

即使本節的某些部分在您第一次閱讀時可能無法理解,我們認為在深入探討之前強調一些陷阱非常重要,因此請繼續閱讀

Unicode 支援是一項廣泛的要求。雖然 Perl 沒有從頭到尾實作 Unicode 標準或附帶技術報告,但 Perl 支援許多 Unicode 功能。

此外,使用 Unicode 可能會出現不顯而易見的安全問題,請參閱下方的「Unicode 的安全性影響」。

如果您使用 use feature 'unicode_strings' 會最安全

為了保持向後相容性,除非指定語用 use feature 'unicode_strings',否則 Perl 不會開啟完整的內部 Unicode 支援。(如果您 use v5.12 或更高版本,這會自動選取。)未執行此操作可能會觸發意外的驚喜。請參閱下方的「Unicode 錯誤」。

此語用不會影響 I/O。它也不會變更字串的內部表示,只會變更其解釋。仍有幾個地方不完全支援 Unicode,例如在檔案名稱中。

輸入和輸出層

使用 :encoding(...) 層使用指定的編碼從檔案控制代碼讀取和寫入檔案控制代碼。(請參閱 open。)

您必須將非 ASCII、非 UTF-8 Perl 腳本轉換為 UTF-8。

自 perl 5.18 起,編碼 模組已被棄用,而 perl 5.26 已移除其所需的 perl 內部元件。

在腳本中啟用 UTF-8 仍需要use utf8

如果 Perl 腳本本身以 UTF-8 編碼,則必須明確包含 use utf8 實用指令,以啟用對其的辨識(在字串或正規表示式字面值中,或在識別名稱中)。這是唯一需要明確使用 use utf8 的時候。(請參閱 utf8)。

如果 Perl 腳本以形成 Unicode 位元組順序標記 (BOM,請參閱 "Unicode 編碼") 的 UTF-8 編碼的位元組開頭,則這些位元組將被完全忽略。

自動偵測 UTF-16 腳本

如果 Perl 腳本以 Unicode BOM(UTF-16LE、UTF16-BE)開頭,或者如果腳本看起來像非 BOM 標記的任何位元序的 UTF-16,Perl 將正確地以適當的 Unicode 編碼讀取腳本。

位元組和字元語意

在 Unicode 之前,大部分編碼都使用 8 位元(一個位元組)來編碼每個字元。因此,一個字元是一個位元組,一個位元組是一個字元,而且可能只有 256 個或更少的可能字元。此節標題中的「位元組語意」是指這種行為。沒有必要區分「位元組」和「字元」。

然後出現了 Unicode,它可以容納超過一百萬個字元(而 Perl 允許更多)。這表示一個字元可能需要超過一個位元組來表示它,因此這兩個術語不再等同。重要的是整個實體的字元,而不是通常組成它們的位元組。這就是此節標題中「字元語意」一詞所指的內容。

Perl 必須在內部進行變更,才能將「位元組」與「字元」分開。如果您尚未這樣做,重要的是您也要改變您的想法,以便「位元組」和「字元」在您的腦海中不再具有相同的意義。

Perl 字串的基本建構區塊一直都是「字元」。這些變更基本上歸結為實作不再認為一個字元總是只是一個位元組。

有幾件事需要注意

重點是 Perl 一直實踐「字元語意」,但隨著 Unicode 的出現,這現在與「位元組語意」不同。

ASCII 規則與 Unicode 規則

在 Unicode 之前,當一個字元為一個位元組時,Perl 只知道 ASCII 定義的 128 個字元,也就是碼點 0 到 127(在 use locale 下除外)。這讓碼點 128 到 255 未被指定,並可供程式使用。它們唯一的語意是它們的序數,而且它們不屬於任何非負字元類別。例如,沒有任何一個被認為與 \w 相符,但所有都與 \W 相符。

當然,Unicode 會將這些碼點中的每個碼點指定為特定含義(以及 255 以上的碼點)。為了保持向後相容性,Perl 僅在有跡象表明打算使用 Unicode 時才使用 Unicode 含義;否則,非 ASCII 碼點仍會被視為未指派。

以下是 Perl 判斷字串應視為 Unicode 的方式

請注意,上述所有內容都會在 use bytes 範圍內被覆寫;但您應該只將此語法用於除錯。

另請注意,與平台作業系統的一些互動從未使用 Unicode 規則。

當 Unicode 規則生效時

延伸的音節群集(邏輯字元)

考慮一個字元,例如 H。它可能出現各種標記,例如在上面、下面、一邊或另一邊、等等的尖音符號、抑揚符號或各種鉤子、圓圈、箭頭,等等。世界語言中有許多可能性。組合數量龐大,如果每個組合都有字元,很快就會用盡 Unicode 超過一百萬個可能的字元。因此,Unicode 採取不同的方法:有一個字元代表基礎 H,每個可能的標記有一個字元,這些字元可以組合以取得最終的邏輯字元。因此,邏輯字元(看起來像單一字元)可以是多個個別字元的序列。Unicode 標準稱這些為「延伸字形群集」(這是已不再廣泛使用的「字形群集」的改良版本);Perl 提供 \X 正規表示式結構,以完全比對這些序列。

但 Unicode 的目的是統一現有字元集標準和實務,而且幾個現有標準有單一字元,其意思與這些組合相同,例如 ISO-8859-1,其中有相當多這些組合。例如,「帶尖音符號的拉丁大寫字母 E」在 Unicode 出現時已在這個標準中。因此,Unicode 將其新增到其曲目中,作為那個單一字元。但 Unicode 認為這個字元等於由字元 「拉丁大寫字母 E」 後接字元 「組合尖音符號」 組成的序列。

"拉丁大寫字母 E 帶銳音符號" 被稱為「預組成」字元,而它與「E」和「組合重音符號」序列的等價性稱為標準等價性。所有預組成字元都說具有分解(成等價序列),而分解類型也被稱為標準。字串可以盡可能由預組成字元組成,或者可以完全由分解字元組成。Unicode 分別稱這些為「正規化形式組成」(NFC) 和「正規化形式分解」。Unicode::Normalize 模組包含用於在兩者之間轉換的函式。字串也可以同時具有組成字元和分解字元;這個模組可被用於讓它全部成為其中之一。

您可能會以任何這些等價形式呈現字串。Perl 5 中目前沒有任何內容會忽略差異。因此,您必須特別處理它。通常的建議是在進一步處理之前將您的輸入轉換為 NFD

如需更詳細資訊,請參閱 http://unicode.org/reports/tr15/

Unicode 字元屬性

(Perl 認為個別碼點序列為單一邏輯字元的唯一時間是在 \X 建構中,如上所述。因此,此討論中的「字元」表示單一 Unicode 碼點。)

幾乎所有 Unicode 字元屬性都可以透過正規表示法存取,方法是使用 \p{}「符合屬性」建構和 \P{}「不符合屬性」作為其否定。

例如,\p{Uppercase} 符合任何具有 Unicode 「Uppercase」 屬性的單一字元,而 \p{L} 符合任何具有 「L」(字母)屬性的 General_Category 字元(請參閱下方的 「General_Category」)。單一字母屬性名稱不需要括號,因此 \p{L} 等於 \pL

更正式地說,\p{Uppercase} 符合任何 Unicode Uppercase 屬性值為 True 的單一字元,而 \P{Uppercase} 符合任何 Uppercase 屬性值為 False 的字元,它們可以分別寫成 \p{Uppercase=True}\p{Uppercase=False}

當屬性不是二進位時,需要這種形式;也就是說,如果屬性可以採用多於 TrueFalse 的值。例如,Bidi_Class 屬性(請參閱下方的「雙向字元類型」),可以採用多個不同的值,例如 LeftRightWhitespace 等。若要比對這些值,需要同時指定屬性名稱 (Bidi_Class) 和要比對的值 (LeftRight 等)。這項作業如同上述範例,讓兩個元件以等號(或可互換使用的冒號)分隔,例如 \p{Bidi_Class: Left}

所有 Unicode 定義的字元屬性都可以寫成 \p{property=value}\p{property:value} 的複合形式,但 Perl 提供一些額外的屬性,僅以單一形式撰寫,以及所有二進位屬性和其他某些屬性的單一形式捷徑(如下所述),其中您可以省略屬性名稱和等號或冒號分隔符號。

大多數 Unicode 字元屬性至少有兩個同義詞(或別名,如果您喜歡):一個較短、較容易輸入,另一個較長、較具描述性,因此較容易理解。因此,上述的 "L""Letter" 屬性是等效的,可以互換使用。同樣地,"Upper""Uppercase" 的同義詞,我們可以將 \p{Uppercase} 等效地寫成 \p{Upper}。此外,屬性可以採用的值通常有各種同義詞。對於二進位屬性,"True" 有 3 個同義詞:"T""Yes""Y";而 "False" 相應地有 "F""No""N"。但請小心。某個屬性的值的簡寫形式可能與另一個屬性的相同拼寫的簡寫形式不同。因此,對於 "General_Category" 屬性,"L" 表示 "Letter",但對於 Bidi_Class 屬性,"L" 表示 "Left"。屬性和同義詞的完整清單在 perluniprops 中。

屬性名稱和值中的大小寫差異無關緊要;因此,\p{Upper} 的意思與 \p{upper} 甚至 \p{UpPeR} 相同。同樣地,您可以在單字中間的任何位置新增或減掉底線,因此這些也等於 \p{U_p_p_e_r}。而空白通常與非單字字元無關,例如大括號和等號或冒號分隔符號,因此 \p{ Upper }\p{ Upper_case : Y } 也等於這些。事實上,通常可以在任何地方新增或刪除空白甚至連字號。因此,即使 \p{ Up-per case = Yes} 也等效。Unicode 將這一切稱為「寬鬆比對」。由於少數異常值,因此「名稱」屬性對此有一些限制。詳細資訊載於 https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2

使用較嚴格比對的幾個地方在數字中間、「名稱」屬性,以及以底線開頭或結尾的 Perl 延伸屬性。較嚴格的比對會在意空白(非單字字元相鄰處除外)、連字號和非內部底線。

您也可以在 \p{}\P{} 中使用否定,方法是在第一個大括號和屬性名稱之間插入插入符號 (^):\p{^Tamil} 等於 \P{Tamil}

幾乎所有屬性都對大小寫不敏感比對免疫。也就是說,新增 /i 正規表示法修飾符號不會改變它們的比對結果。有兩組會受到影響。第一組是 Uppercase_LetterLowercase_LetterTitlecase_Letter,所有這些在 /i 比對下都比對 Cased_Letter。第二組是 UppercaseLowercaseTitlecase,所有這些在 /i 比對下都比對 Cased。此組也包含其子集 PosixUpperPosixLower,兩者在 /i 下都比對 PosixAlpha。(這些組之間的差異在於某些事物,例如羅馬數字,有大小寫之分,因此它們是 Cased,但不被視為字母,因此它們不是 Cased_Letter。)

請參閱"Unicode 碼點之外",了解在將 Unicode 屬性與非 Unicode 碼點配對時應注意的事項。

General_Category

每個 Unicode 字元都指定了一個一般類別,這是「字元的常見分類」(來自 https://www.unicode.org/reports/tr44)。

撰寫這些的複合方式類似於 \p{General_Category=Number}(簡寫:\p{gc:n})。但 Perl 提供捷徑,其中等號或冒號分隔符號之前的所有內容都會省略。因此,您也可以只寫 \pN

以下是 General Category 屬性可以具有的值的簡寫和長寫

Short       Long

L           Letter
LC, L&      Cased_Letter (that is: [\p{Ll}\p{Lu}\p{Lt}])
Lu          Uppercase_Letter
Ll          Lowercase_Letter
Lt          Titlecase_Letter
Lm          Modifier_Letter
Lo          Other_Letter

M           Mark
Mn          Nonspacing_Mark
Mc          Spacing_Mark
Me          Enclosing_Mark

N           Number
Nd          Decimal_Number (also Digit)
Nl          Letter_Number
No          Other_Number

P           Punctuation (also Punct)
Pc          Connector_Punctuation
Pd          Dash_Punctuation
Ps          Open_Punctuation
Pe          Close_Punctuation
Pi          Initial_Punctuation
            (may behave like Ps or Pe depending on usage)
Pf          Final_Punctuation
            (may behave like Ps or Pe depending on usage)
Po          Other_Punctuation

S           Symbol
Sm          Math_Symbol
Sc          Currency_Symbol
Sk          Modifier_Symbol
So          Other_Symbol

Z           Separator
Zs          Space_Separator
Zl          Line_Separator
Zp          Paragraph_Separator

C           Other
Cc          Control (also Cntrl)
Cf          Format
Cs          Surrogate
Co          Private_Use
Cn          Unassigned

單字母屬性與以相同字母開頭的任何兩個字母子屬性中的所有字元相符。LCL& 是特殊的:兩者都是別名,代表由 LlLuLt 相符的所有內容組成的集合。

雙向字元類型

因為腳本在方向性上有所不同(例如希伯來語和阿拉伯語從右到左書寫),Unicode 提供 Bidi_Class 屬性。此屬性可以具有的值包括

Value       Meaning

L           Left-to-Right
LRE         Left-to-Right Embedding
LRO         Left-to-Right Override
R           Right-to-Left
AL          Arabic Letter
RLE         Right-to-Left Embedding
RLO         Right-to-Left Override
PDF         Pop Directional Format
EN          European Number
ES          European Separator
ET          European Terminator
AN          Arabic Number
CS          Common Separator
NSM         Non-Spacing Mark
BN          Boundary Neutral
B           Paragraph Separator
S           Segment Separator
WS          Whitespace
ON          Other Neutrals

此屬性總是寫成複合形式。例如,\p{Bidi_Class:R} 符合通常從右到左書寫的字元。與 "General_Category" 屬性不同,此屬性可以在未來的 Unicode 版本中新增更多值。上述列出的值組成許多 Unicode 版本的完整集合,但 Unicode 6.3 中新增了其他值;您可以在 perluniprops 中隨時找到目前的屬性。而 https://www.unicode.org/reports/tr9/ 說明如何使用這些屬性。

腳本

世界各地的語言使用許多不同的腳本來書寫。這句話(除非您正在閱讀翻譯版本)是用拉丁字母書寫的,而俄語是用西里爾字母書寫的,希臘語是用希臘字母書寫的;日語主要是用平假名或片假名書寫的。還有許多其他語言。

Unicode ScriptScript_Extensions 屬性指出特定字元屬於哪種腳本。Script_Extensions 屬性是 Script 的改良版本,如下所示。這兩個屬性都可以用複合形式指定,例如 \p{Script=Hebrew}(簡寫:\p{sc=hebr})或 \p{Script_Extensions=Javanese}(簡寫:\p{scx=java})。此外,Perl 為所有 Script_Extensions 屬性名稱提供捷徑。您可以省略等號(或冒號)之前的所有內容,並簡單地寫成 \p{Latin}\P{Cyrillic}。(這不適用於 Script,它必須寫成複合形式。在 Perl v5.26 之前,單一形式會傳回舊版的 Script 版本,但後來已變更,因為 Script_Extensions 提供更好的結果。)

這兩個屬性之間的差異涉及在多個腳本中使用的字元。例如,數字「0」到「9」在世界許多地方使用。這些數字被放置在名為 Common 的腳本中。其他字元僅在少數腳本中使用。例如,「KATAKANA-HIRAGANA DOUBLE HYPHEN」 在日文字母的片假名和平假名中使用,但其他地方沒有。Script 屬性將在多個腳本中使用的所有字元放置在 Common 腳本中,而 Script_Extensions 屬性將僅在少數腳本中使用的字元放置在每個腳本中;同時仍然對在多個腳本中使用的字元使用 Common。因此,這兩個都相符

"0" =~ /\p{sc=Common}/     # Matches
"0" =~ /\p{scx=Common}/    # Matches

而且只有第一個相符

"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Common}  # Matches
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Common} # No match

而且只有最後兩個相符

"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Hiragana}  # No match
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Katakana}  # No match
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Hiragana} # Matches
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Katakana} # Matches

因此,Script_Extensions 是改良過的 Script,其中 Common 腳本中的字元較少,而其他腳本中的字元相對較多。它是 Unicode 版本 6.0 中的新功能,隨著問題的解決,其資料在後續版本中可能會發生重大變更。新程式碼可能應該使用 Script_Extensions 而不是純粹的 Script。如果您使用沒有 Script_Extensions 的 Unicode 版本編譯 perl,則單一形式的 Perl 延伸模組將改為參考純粹的 Script 屬性。如果您使用沒有 Script 屬性的 Unicode 版本編譯,則這些延伸模組將完全不會被定義。

(實際上,除了 Common 之外,Inherited 範疇包含多個範疇使用的字元。這些是繼承控制字元範疇值的修飾字元。其中一些字元用於多個範疇,因此會同時納入 ScriptScript_Extensions 中的 Inherited。其他字元僅用於少數範疇,因此會納入 Script 中的 Inherited,但不會納入 Script_Extensions 中的 Inherited。)

值得強調的是,Unicode 中有數組不同的數字等同於 0-9,且可以在正規表示式中透過 \d 來比對。如果這些數字僅用於單一語言,則會納入該語言的 ScriptScript_Extensions。如果這些數字用於多個範疇,則會納入 sc=Common,但只有在用於多個範疇時,才會納入 scx=Common

上述說明已省略一些細節;請參閱 UAX#24「Unicode Script 屬性」:https://www.unicode.org/reports/tr24

範疇及其捷徑的完整清單請參閱 perluniprops

使用 "Is" 前置詞

為了向後相容(與舊版的 Perl 5.6 相容),所有不使用前面提到的複合形式即可寫入的屬性,其名稱前都可以加上 IsIs_,因此例如 \P{Is_Lu} 等於 \P{Lu},而 \p{IsScript:Arabic} 等於 \p{Arabic}

區塊

除了腳本之外,Unicode 也定義字元的區塊。腳本與區塊的差異在於,腳本的概念較接近自然語言,而區塊的概念則較像是基於連續序數值的一群 Unicode 字元所做的分類。例如,「基本拉丁文」區塊就是所有序數值介於 0 到 127(含)的字元,換句話說,就是 ASCII 字元。「拉丁文」腳本包含了這個區塊以及其他幾個區塊中的一些字母,例如 「拉丁文-1 補充」「拉丁文擴充-A」等等,但它並不包含這些區塊中的所有字元。例如,它不包含數字 0-9,因為這些數字是許多腳本共用的,因此在 「通用」腳本中。

若要進一步了解腳本與區塊,請參閱 UAX#24「Unicode 腳本屬性」:https://www.unicode.org/reports/tr24

在處理自然語言時,您可能需要使用 Script_ExtensionsScript 屬性;Block 屬性偶爾會在處理 Unicode 的基礎架構時派上用場。

區塊名稱會以複合形式進行比對,例如 \p{Block: Arrows}\p{Blk=Hebrew}。與大多數其他屬性不同的是,只有少數區塊名稱有 Unicode 定義的簡稱。

Perl also defines single form synonyms for the block property in cases where these do not conflict with something else. But don't use any of these, because they are unstable. Since these are Perl extensions, they are subordinate to official Unicode property names; Unicode doesn't know nor care about Perl's extensions. It may happen that a name that currently means the Perl extension will later be changed without warning to mean a different Unicode property in a future version of the perl interpreter that uses a later Unicode release, and your code would no longer work. The extensions are mentioned here for completeness: Take the block name and prefix it with one of: In (for example \p{Blk=Arrows} can currently be written as \p{In_Arrows}); or sometimes Is (like \p{Is_Arrows}); or sometimes no prefix at all (\p{Arrows}). As of this writing (Unicode 9.0) there are no conflicts with using the In_ prefix, but there are plenty with the other two forms. For example, \p{Is_Hebrew} and \p{Hebrew} mean \p{Script_Extensions=Hebrew} which is NOT the same thing as \p{Blk=Hebrew}. Our advice used to be to use the In_ prefix as a single form way of specifying a block. But Unicode 8.0 added properties whose names begin with In, and it's now clear that it's only luck that's so far prevented a conflict. Using In is only marginally less typing than Blk:, and the latter's meaning is clearer anyway, and guaranteed to never conflict. So don't take chances. Use \p{Blk=foo} for new code. And be sure that block is what you really really want to do. In most cases scripts are what you want instead.

區塊的完整清單請參閱 perluniprops

其他屬性

這裡描述的屬性還有很多。完整的清單在 perluniprops

Unicode 以複合形式定義所有屬性,所以所有單一形式屬性都是 Perl 延伸。其中大部分只是 Unicode 屬性的同義詞,但有些是真正的延伸,包括幾個複合形式的。這些屬性中有很多實際上是 Unicode 建議的(在 https://www.unicode.org/reports/tr18)。

本節提供所有延伸的詳細資料,這些延伸不只是複合形式 Unicode 屬性的同義詞(對於那些屬性,您必須參閱 Unicode 標準)。

\p{All}

這符合每個可能的代碼點。它等於 qr/./s。與所有其他非使用者定義 \p{} 屬性匹配不同,如果此屬性與非 Unicode 代碼點匹配,則永遠不會產生警告(請參閱下方的「超越 Unicode 代碼點」)。

\p{Alnum}

這符合任何 \p{Alphabetic}\p{Decimal_Number} 字元。

\p{Any}

這符合 1_114_112 個 Unicode 代碼點中的任何一個。它是 \p{Unicode} 的同義詞。

\p{ASCII}

這會比對美國標準資訊交換碼字元集中的 128 個字元,這是 Unicode 的子集。

\p{Assigned}

這會比對任何已指派的碼點;也就是說,任何其一般類別不是 Unassigned(或等同地,不是 Cn)的碼點。

\p{Blank}

這與 \h\p{HorizSpace} 相同:會改變間距的字元。

\p{Decomposition_Type: Non_Canonical} (簡寫:\p{Dt=NonCanon})

比對具有任何非規範分解類型的字元。規範分解在上面的"擴充字形叢集 (邏輯字元)"區段中介紹。然而,許多其他字元具有不同類型的分解,一般稱為「相容」分解或「非規範」分解。形成這些分解的序列不被視為與預先組合字元規範等價。一個範例是 "SUPERSCRIPT ONE"。它有點像一般的數字 1,但又不完全相同;它分解成數字 1 的部分稱為「相容」分解,特別是「上標」(用於「上標」)分解。有許多這樣的相容分解(請參閱 https://www.unicode.org/reports/tr44)。\p{Dt: Non_Canon}是 Perl 擴充,僅使用一個名稱來指稱它們的聯集。

大多數 Unicode 字元沒有分解,因此它們的分解類型為 "None"。因此,Non_Canonical 等同於

qr/(?[ \P{DT=Canonical} - \p{DT=None} ])/

(請注意,其中一種非正規分解稱為「compat」,或許可以改用「雜項」這個更好的名稱。它僅包含 Unicode 無法找出更佳通用名稱的項目。)

\p{Graph}

符合任何圖形字元。理論上,這表示印表機上會使用墨水來印出的字元。

\p{HorizSpace}

這與 \h\p{Blank} 相同:會水平變更間距的字元。

\p{In=*}

這是 \p{Present_In=*} 的同義字。

\p{PerlSpace}

這與 \s 相同,僅限於 ASCII,即 [ \f\n\r\t],從 Perl v5.18 開始,還包括垂直定位標籤。

助記符:Perl 的(原始)空白

\p{PerlWord}

這與 \w 相同,僅限於 ASCII,即 [A-Za-z0-9_]

助記符:Perl 的(原始)字詞。

\p{Posix...}

有幾個等效的項目,使用 \p{} 符號表示,用於 Posix 類別,並在 "perlrecharclass 中的 POSIX 字元類別" 中說明。

\p{Present_In: *} (簡寫:\p{In=*})

當您需要知道字元出現在哪一個或哪些 Unicode 版本中時,會使用這個屬性。

上方的「*」代表某些 Unicode 版本號碼,例如 1.112.0;「*」也可以是 Unassigned。此屬性將比對最終處置已在版本號碼所提供的 Unicode 發行版中確定的碼點;\p{Present_In: Unassigned} 將比對尚未指派意義的碼點。

例如,U+0041 "LATIN CAPITAL LETTER A" 出現在第一個可用的 Unicode 發行版中,也就是 1.1,因此此屬性對於所有有效的「*」版本都是正確的。另一方面,U+1EFF 直到版本 5.1 才指派,當時它變成了 "LATIN SMALL LETTER Y WITH LOOP",因此唯一會比對它的「*」是 5.1、5.2,以及後續版本。

Unicode 提供 Age 屬性,此屬性是衍生屬性。Age 的問題在於嚴格的詮釋(Perl 採用此詮釋)讓它比對碼點意義引入的確切發行版。因此,U+0041 僅會比對 1.1;而 U+1EFF 僅會比對 5.1。這通常不是您想要的。

Age 屬性的一些非 Perl 實作可能會變更其意義,使其與 Perl Present_In 屬性相同;請注意這一點。

這兩個屬性另一個令人困惑的地方在於,定義並非碼點已指派,而是碼點的意義已確定。這是因為 66 個碼點將永遠不會指派,因此它們的 Age 是 Unicode 在其中做出此決定的版本。例如,U+FDD0 將永久不指派給字元,而做出此決定的版本是 3.1,因此 \p{Age=3.1} 比對此字元,\p{Present_In: 3.1} 及後續版本也比對此字元。

\p{Print}

這會比對任何圖形或空白字元,控制字元除外。

\p{SpacePerl}

這與 \s 相同,包含 ASCII 以外的字元。

助記符:空白,由 Perl 修改。(它不包含垂直定位標籤,直到 v5.18,而 Posix 標準和 Unicode 都將其視為空白。)

\p{Title}\p{Titlecase}

在大寫小寫敏感的比對中,這兩個都比對與 \p{General Category=Titlecase_Letter} (\p{gc=lt}) 相同的碼點。不同之處在於,在 /i 大小寫不敏感的比對中,這兩個比對與 \p{Cased} 相同,而 \p{gc=lt} 比對 \p{Cased_Letter

\p{Unicode}

這會比對 1_114_112 個 Unicode 碼點中的任何一個。\p{Any}

\p{VertSpace}

這與 \v 相同:一個垂直改變間距的字元。

\p{Word}

這與 \w 相同,包含超過 100_000 個 ASCII 以外的字元。

\p{XPosix...}

有幾個這樣的字元類別,它們是標準 Posix 類別,已擴充至完整的 Unicode 範圍。它們在 "POSIX Character Classes" in perlrecharclass 中有說明。

\N{...}\p{name=...} 的比較

從 Perl 5.32 開始,您可以使用 \p{name=...} 在正規表示式模式中透過名稱指定字元。這是除了長久以來使用 \N{...} 的方法之外的另一種方法。以下是這兩種方法的差異摘要

                      \N{...}       \p{Name=...}
can interpolate    only with eval       yes            [1]
custom names            yes             no             [2]
name aliases            yes             yes            [3]
named sequences         yes             yes            [4]
name value parsing     exact       Unicode loose       [5]
[1]

插補功能表示您可以執行類似以下的動作

qr/\p{na=latin capital letter $which}/

並在其他地方指定 $which

[2]

您可以為字元建立自己的名稱,並在使用 \N{...} 時覆寫官方名稱。請參閱 charnames 中的「自訂別名」

[3]

有些字元有多個名稱(同義詞)。

[4]

除了個別名稱之外,某些特定字元序列也會獲得單一名稱。

[5]

精確名稱值比對表示您必須精確地指定您要的名稱中的大小寫、連字號、底線和空格。寬鬆比對遵循 Unicode 規則 https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2,其中這些規則大多無關緊要。除了少數例外字元名稱之外,這些規則與已用於任何其他 \p{...} 屬性的規則相同。

屬性值中的萬用字元

從 Perl 5.30 開始,可以執行類似以下的動作

qr!\p{numeric_value=/\A[0-5]\z/}!

或者,透過縮寫並加入 /x

qr! \p{nv= /(?x) \A [0-5] \z / }!

這會比對數值為 0、1、2、3、4 或 5 的所有碼點。這個特定範例在較早的 perl 中可以寫成

qr! \A [ \p{nv=0}\p{nv=1}\p{nv=2}\p{nv=3}\p{nv=4}\p{nv=5} ] \z !xx

因此,在這種情況下,這個功能只是讓寫作更輕鬆且簡短。如果我們沒有包含 \A\z,這些會比對類似 1/2 的項目,因為它包含 1(以及 2)。根據寫法,它會比對具有這些數值的項目,例如下標。如果我們只想要具有這些數值的十進位數字,我們可以說,

qr! (?[ \d & \p{nv=/[0-5]/ ]) }!x

\d 會讓模式不需要錨定,因為它會強制結果只符合 [0-9],而 [0-5] 會進一步限制它。

以上範例中包含在 "/" 字元之間的文字可以是任何正規表示式。它獨立於主要模式,因此不會共用任何擷取群組、。它的分隔符號必須是 ASCII 標點符號,但不能用 "{""}" 分隔,也不能包含字面上的 "}",因為那是用來分隔封閉的 \p{} 的結尾。就像任何模式一樣,某些其他分隔符號會以它們的鏡像符號終止。這些符號是 "(""[""<"。如果分隔符號是 "-""_""+""\" 中的任何一個,或是與用於封閉模式的分隔符號相同,則它必須前後加上反斜線跳脫字元。

小心使用 "$" 來表示與字串結尾相符。它很容易被解釋為標點符號變數,例如 $/

最後一個分隔符號後面不能接修飾詞。請改用 perlre 中的「(?adlupimnsx-imnsx)」 和/或 perlre 中的「(?adluimnsx-imnsx:pattern)」 來指定修飾詞。不過,某些修飾詞在您的萬用字元子模式中是非法的。唯一可以指定的字元集修飾詞是 /aa;任何其他字元集、-mps 都是非法的。指定像 qr/.../gc 這樣的修飾詞(在 (?...) 符號中是非法的)通常會產生警告,但對於萬用字元子模式,使用它們會產生錯誤。m 修飾詞無效;所有符合條件的內容都會成為單行。

預設情況下,您的模式會進行大小寫不敏感的比對,就像指定了 /i 一樣。您可以在模式中使用 (?-i) 來變更此設定。

還有某些運算是非法的。您不能在萬用字元子模式中嵌套 \p{...}\P{...} 呼叫,而 \G 沒有意義,因此也禁止使用。

而且 * 量詞(或其等效的 (0,})是非法的。

當左側加上 Is_ 前綴,或任何在 perluniprops 中標示為「不建議使用」 的形式時,此功能不可用。

此實驗功能已新增,以開始實作 https://www.unicode.org/reports/tr18/#Wildcard_Properties。使用它會在 experimental::uniprop_wildcards 類別中提出警告(預設開啟)。我們保留在獲得經驗時變更其運作方式的權利。

您的子模式幾乎可以是任何東西,但為了讓它具備一些效用,它應該在使用下列任一或兩者時相符:a) 屬性值的完整名稱,包含底線(和/或區塊屬性中的空格),以及某些大寫項目;或 b) 屬性值全部小寫,且空格和底線已移除。例如,

qr!\p{Blk=/Old I.*/}!
qr!\p{Blk=/oldi.*/}!

會相符於相同項目。

另一個範例顯示在 \p{...} 內,/x 不需要空格

qr!\p{scx= /Hebrew|Greek/ }!

為了安全起見,我們應該已將上述範例錨定,以防止相符於類似 Hebrew_Braille 的項目,但目前沒有任何類似的腳本名稱。如果您的模式未相符於屬性的任何合法值,則會發出警告。您的模式最終導致每個可能的碼點都相符時,未來版本可能會提出警告。

從 5.32 開始,允許比對名稱、名稱別名和命名序列屬性。它們被視為單一組合屬性,就像長期以來對 \N{} 的處理方式一樣。這些屬性的寬鬆比對方式與其他屬性值的寬鬆比對方式並不完全相同。規則載於 https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2。因此,Perl 不同於其他屬性,不會嘗試為您進行寬鬆比對。名稱中的所有字母均為大寫,但您可以在子模式中加入 (?i) 來忽略大小寫。如果您不確定空白在哪裡,可以在子模式中使用 ?。沒有字元名稱包含底線,因此不要嘗試比對底線。連字號的使用特別有問題;請參閱上述連結。但請注意,截至 Unicode 13.0,現代使用中唯一有這些怪異現象的文字是藏文;另外還有兩個韓文字元 U+116C 諺文重聲符 OE 和 U+1180 諺文重聲符 O-E。Unicode 不保證未來不會加入有連字號問題的名稱。

由於必須檢查數十萬個合法名稱,因此對這些名稱使用萬用字元會消耗大量資源。

使用名稱屬性萬用字元的範例為

qr!\p{name=/(SMILING|GRINNING) FACE/}!

另一個範例為

qr/(?[ \p{name=\/CJK\/} - \p{ideographic} ])/

這是截至 Unicode 13.0 的 200 多個非表意文字的 CJK 字元。

有某些屬性目前無法與萬用字元子模式搭配使用。這些屬性包括

Bidi Mirroring Glyph
Bidi Paired Bracket
Case Folding
Decomposition Mapping
Equivalent Unified Ideograph
Lowercase Mapping
NFKC Case Fold
Titlecase Mapping
Uppercase Mapping

@unicode_property@ 表單也未實作。

以下是一個在任何(單一)腳本中比對 IPV4 網際網路協定的完整範例

no warnings 'experimental::uniprop_wildcards';

# Can match a substring, so this intermediate regex needs to have
# context or anchoring in its final use.  Using nt=de yields decimal
# digits.  When specifying a subset of these, we must include \d to
# prevent things like U+00B2 SUPERSCRIPT TWO from matching
my $zero_through_255 =
 qr/ \b (*sr:                                  # All from same sript
           (?[ \p{nv=0} & \d ])*               # Optional leading zeros
       (                                       # Then one of:
                                 \d{1,2}       #   0 - 99
           | (?[ \p{nv=1} & \d ])  \d{2}       #   100 - 199
           | (?[ \p{nv=2} & \d ])
              (  (?[ \p{nv=:[0-4]:} & \d ]) \d #   200 - 249
               | (?[ \p{nv=5}     & \d ])
                 (?[ \p{nv=:[0-5]:} & \d ])    #   250 - 255
              )
       )
     )
   \b
 /x;

my $ipv4 = qr/ \A (*sr:         $zero_through_255
                        (?: [.] $zero_through_255 ) {3}
                  )
               \z
           /x;

使用者定義的字元屬性

您可以透過定義名稱以 "In""Is" 開頭的子常式來定義您自己的二進位字元屬性。(regex 組合功能 "(?[ ])" in perlre 提供了一個可允許更複雜定義的替代方案。)子常式可以在任何套件中定義。它們會覆寫任何表示為相同名稱的 Unicode 屬性。使用者定義的屬性可以在正規表示式 \p{}\P{} 結構中使用;如果您正在使用來自您所在套件以外的使用者定義屬性,您必須在 \p{}\P{} 結構中指定其套件。

# assuming property IsForeign defined in Lang::
package main;  # property package name required
if ($txt =~ /\p{Lang::IsForeign}+/) { ... }

package Lang;  # property package name not required
if ($txt =~ /\p{IsForeign}+/) { ... }

子常式會傳遞一個單一參數,如果區分大小寫比對有效,則為 0,如果不區分大小寫比對有效,則為非 0。子常式可能會根據標記的值傳回不同的值。但子常式對於每個標記值(0 與非 0)只會被呼叫一次。傳回值會被儲存並使用,而不是再次呼叫子常式。如果在編譯模式時定義子常式,它會在當時被呼叫;如果不是,它會在執行期間第一次需要其值(對於該標記)時被呼叫。

請注意,如果正規表示式受到污染,則 Perl 會在子常式名稱由受污染資料決定時立即終止,而不是呼叫子常式。

子常式必須傳回一個特別格式化的字串,包含一個或多個換行分隔的列。每列必須符合下列其中一項

例如,若要定義涵蓋日文音節表(平假名和片假名)的屬性,您可以定義

sub InKana {
    return <<END;
3040\t309F
30A0\t30FF
END
}

假設這裡文件結束標記位於行開頭。現在您可以使用 \p{InKana}\P{InKana}

您也可以使用現有的區塊屬性名稱

sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
END
}

假設您只想比對已配置字元,而非原始區塊範圍:換句話說,您要移除未指派字元

sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
-utf8::IsCn
END
}

否定可用於定義(驚喜!)否定類別。

sub InNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
END
}

這將比對所有非 Unicode 碼點,因為每個碼點都不在日文音節表中。如果您希望排除這些碼點,可以使用交集,如下修改範例所示

sub InNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
&utf8::Any
END
}

&utf8::Any 必須是定義中的最後一行。

交集通常用於取得由兩個(或更多)類別比對到的共用字元。請務必記住,不要將 "&" 用於第一個集合;這會與空值相交,導致空集合。(類似地,將 "-" 用於第一個集合不會產生任何作用)。

與非使用者定義的 \p{} 屬性比對不同,如果這些屬性與非 Unicode 碼點比對,將不會產生任何警告(請參閱下方的「超越 Unicode 碼點」)。

使用者定義大小寫對應(僅限於資深駭客)

此功能已從 Perl 5.16 中移除。 CPAN 模組 Unicode::Casing 提供更好的功能,且沒有此功能的缺點。如果您使用的是早於 5.16 的 Perl,此功能在 5.14 版本的 pod 中有最完整的說明:https://perldoc.dev.org.tw/5.14.0/perlunicode.html#User-Defined-Case-Mappings-%28for-serious-hackers-only%29

輸入和輸出的字元編碼

請參閱 Encode

Unicode 正規表示式支援層級

以下是針對正規表示式支援的 Unicode 功能清單,說明 Perl 核心目前直接支援的所有功能。對「層級 N」和區段編號的參考請參閱 UTS#18「Unicode 正規表示式」,第 18 版,2016 年 10 月。

層級 1 - 基本 Unicode 支援

RL1.1   Hex Notation                     - Done          [1]
RL1.2   Properties                       - Done          [2]
RL1.2a  Compatibility Properties         - Done          [3]
RL1.3   Subtraction and Intersection     - Done          [4]
RL1.4   Simple Word Boundaries           - Done          [5]
RL1.5   Simple Loose Matches             - Done          [6]
RL1.6   Line Boundaries                  - Partial       [7]
RL1.7   Supplementary Code Points        - Done          [8]
[1] \N{U+...}\x{...}
[2] \p{...} \P{...}。這個需求是針對最小屬性清單。Perl 支援這些屬性。請參閱 R2.7 以取得其他屬性。
[3]

Perl 有 \d \D \s \S \w \W \X [:prop:] [:^prop:],以及 https://www.unicode.org/reports/tr18/#Compatibility_Properties 指定的所有屬性。這些屬性已在 「其他屬性」 中說明。

[4]

v5.18 開始提供的 regex sets 功能 "(?[...])" 可達成這個目的。請參閱 "(?[ ])" in perlre

[5] \b \B 符合這個需求的大部分但不是全部細節,但 \b{wb}\B{wb} 符合,而且比更嚴格的 R2.3 還要嚴格。
[6]

請注意,Perl 在比對時進行的是完全大小寫轉換,而不是簡易大小寫轉換

例如,U+1F88 等於 U+1F00 U+03B9,而不仅仅是 U+1F80。此差異主要影響某些帶有特定修飾符的希臘大寫字母:全型大小寫轉換會分解字母,而簡式大小寫轉換會將其對應到單一字元。

[7]

此功能被視為僅部分實作的原因,在於 Perl 有 qr/\b{lb}/Unicode::LineBreak,它們符合 UAX#14「Unicode 換行演算法」。正規表示式建構提供預設行為,而較重量級的模組提供可自訂的換行。

但是 Perl 將 \n 視為行首和行尾分隔符,而 Unicode 則指定更多字元應如此詮釋。

這些字元是

VT   U+000B  (\v in C)
FF   U+000C  (\f)
CR   U+000D  (\r)
NEL  U+0085
LS   U+2028
PS   U+2029

正規表示式模式中的 ^$ 應該比對所有這些字元,但並未比對。這些字元也不應該,但會影響 <> $. 和指令碼行號。

此外,不應在 CRLF 內拆分行(亦即 \r\n 之間沒有空行)。對於 CRLF,請嘗試 :crlf 層(請參閱 PerlIO)。

[8] Perl 中使用的 UTF-8/UTF-EBDDIC 不僅允許 U+10000U+10FFFF,還允許超過 U+10FFFF

第 2 級 - 延伸 Unicode 支援

RL2.1   Canonical Equivalents           - Retracted     [9]
                                          by Unicode
RL2.2   Extended Grapheme Clusters and  - Partial       [10]
        Character Classes with Strings
RL2.3   Default Word Boundaries         - Done          [11]
RL2.4   Default Case Conversion         - Done
RL2.5   Name Properties                 - Done
RL2.6   Wildcards in Property Values    - Partial       [12]
RL2.7   Full Properties                 - Partial       [13]
RL2.8   Optional Properties             - Partial       [14]
第 3 層級 - 客製化支援

Unicode 已撤回此內容。

Unicode 編碼

Unicode 字元會指定給碼點,這些碼點是抽象數字。若要使用這些數字,需要各種編碼。

  • UTF-8

    UTF-8 是一種可變長度 (1 至 4 位元組)、與位元組順序無關的編碼。在 Perl 的大多數文件,包括此文件中的其他地方,「UTF-8」一詞也表示「UTF-EBCDIC」。但在本節中,「UTF-8」僅指在 ASCII 平台上使用的編碼。它是 7 位元組 US-ASCII 的超集,因此以 ASCII 編碼的任何內容在以 UTF-8 編碼時具有相同的表示形式。

    下表來自 Unicode 3.2。

    Code Points            1st Byte  2nd Byte  3rd Byte 4th Byte
    
      U+0000..U+007F       00..7F
      U+0080..U+07FF     * C2..DF    80..BF
      U+0800..U+0FFF       E0      * A0..BF    80..BF
      U+1000..U+CFFF       E1..EC    80..BF    80..BF
      U+D000..U+D7FF       ED        80..9F    80..BF
      U+D800..U+DFFF       +++++ utf16 surrogates, not legal utf8 +++++
      U+E000..U+FFFF       EE..EF    80..BF    80..BF
     U+10000..U+3FFFF      F0      * 90..BF    80..BF    80..BF
     U+40000..U+FFFFF      F1..F3    80..BF    80..BF    80..BF
    U+100000..U+10FFFF     F4        80..8F    80..BF    80..BF

    請注意上述幾個位元組條目之前標記為「*」的間隔。這些間隔是由合法的 UTF-8 避免使用非最短編碼所造成的:技術上來說,可以以不同的方式對單一碼點進行 UTF-8 編碼,但這明確地被禁止,而且應始終使用最短的編碼(而這正是 Perl 所做的)。

    另一種觀察方式是透過位元

                   Code Points  1st Byte  2nd Byte  3rd Byte  4th Byte
    
                      0aaaaaaa  0aaaaaaa
              00000bbbbbaaaaaa  110bbbbb  10aaaaaa
              ccccbbbbbbaaaaaa  1110cccc  10bbbbbb  10aaaaaa
    00000dddccccccbbbbbbaaaaaa  11110ddd  10cccccc  10bbbbbb  10aaaaaa

    如您所見,延續位元組全部以 "10" 開頭,而起始位元組的前導位元會說明編碼字元中有多少位元組。

    原始的 UTF-8 規格允許最多 6 個位元組,以編碼高達 0x7FFF_FFFF 的數字。Perl 繼續允許這些數字,並已將其擴充至 13 個位元組,以編碼可放入 64 位元組字元中的碼點。然而,如果您輸出任何這些碼點,Perl 會警告它們不可移植;而在嚴格的 UTF-8 輸入協定下,它們是被禁止的。此外,現在禁止使用大於系統上已簽署整數變數所能容納的碼點。在 32 位元組 ASCII 系統上,這表示 0x7FFF_FFFF 是合法的最大值(在 64 位元組系統上則高得多)。

  • UTF-EBCDIC

    與 UTF-8 類似,但 EBCDIC 安全,UTF-8 是 ASCII 安全的方式。這表示所有基本字元(包括所有具有 ASCII 等效項的字元(例如 "A""0""%" 等)在 EBCDIC 和 UTF-EBCDIC 中都是相同的。)

    UTF-EBCDIC 用於 EBCDIC 平台。它通常需要更多位元組來表示給定的碼點,而不是 UTF-8;最大的 Unicode 碼點需要 5 個位元組來表示(而不是 UTF-8 中的 4 個),並且針對 64 位元組字元擴充後,它使用 14 個位元組,而不是 UTF-8 中的 13 個位元組。

  • UTF-16、UTF-16BE、UTF-16LE、代理字元和 BOM(位元組順序標記)

    下列項目主要用於參考和一般的 Unicode 知識,Perl 在內部不使用這些結構。

    與 UTF-8 相同,UTF-16 是一種可變寬度編碼,但 UTF-8 使用 8 位元組碼元,而 UTF-16 使用 16 位元組碼元。在 UTF-16 中,所有碼點都佔用 2 或 4 個位元組:碼點 U+0000..U+FFFF 儲存在單一 16 位元組單元中,而碼點 U+10000..U+10FFFF 則儲存在兩個 16 位元組單元中。後者使用代理項,第一個 16 位元組單元為高代理項,第二個為低代理項

    代理項是保留的碼點,用於將 Unicode 碼點範圍 U+10000..U+10FFFF 編碼成兩組 16 位元組單元。高代理項的範圍為 U+D800..U+DBFF,而低代理項的範圍為 U+DC00..U+DFFF。代理項編碼為

    $hi = ($uni - 0x10000) / 0x400 + 0xD800;
    $lo = ($uni - 0x10000) % 0x400 + 0xDC00;

    而解碼為

    $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00);

    由於 16 位元組,UTF-16 與位元組順序有關。UTF-16 本身可用於記憶體內運算,但如果需要儲存或傳輸,則必須選擇 UTF-16BE(大端序)或 UTF-16LE(小端序)編碼。

    這引入了另一個問題:如果你只知道你的資料是 UTF-16,但不知道哪個端序怎麼辦?位元組順序標記或 BOM 是這個問題的解決方案。Unicode 中保留了一個特殊字元作為位元組順序標記:碼點為 U+FEFF 的字元就是 BOM

    訣竅在於,如果你讀取 BOM,你就會知道位元組順序,因為如果它寫在一個大端序平台上,你會讀取位元組 0xFE 0xFF,但如果它寫在一個小端序平台上,你會讀取位元組 0xFF 0xFE。(如果原始平台使用 ASCII 平台 UTF-8 寫入,你會讀取位元組 0xEF 0xBB 0xBF。)

    這個訣竅運作的方式是,碼點為 U+FFFE 的字元不應該出現在輸入串流中,因此位元組序列 0xFF 0xFE 明確表示「BOM,以小端序格式表示」,而不能表示「U+FFFE,以大端序格式表示」。

    代理字元在 Unicode 中沒有意義,除非與其他碼點配對使用。然而,Perl 允許它們在內部個別表示,例如說 chr(0xD801),這樣所有碼點,不只那些對開放交換有效的碼點,都是可表示的。Unicode 確實為它們定義語意,例如它們的 "General_Category""Cs"。但因為它們的使用有點危險,如果嘗試對它們進行某些操作,例如取得小寫、不區分大小寫比對,或輸出它們,Perl 會發出警告(使用警告類別 "surrogate",它是 "utf8" 的子類別)。(但不要在 5.14 之前的 Perl 版本上嘗試這樣做。)

  • UTF-32、UTF-32BE、UTF-32LE

    UTF-32 系列很像 UTF-16 系列,不同之處在於單位是 32 位元,因此不需要代理字元方案。UTF-32 是一種固定寬度的編碼。BE 的 BOM 簽章是 0x00 0x00 0xFE 0xFF,LE 的 BOM 簽章是 0xFF 0xFE 0x00 0x00

  • UCS-2、UCS-4

    ISO 10646 標準定義的傳統固定寬度編碼。UCS-2 是一種 16 位元編碼。與 UTF-16 不同,UCS-2 不可延伸至 U+FFFF 之後,因為它不使用代理字元。UCS-4 是一種 32 位元編碼,與 UTF-32 的功能相同(兩者的差異在於 UCS-4 禁止使用代理字元,且禁止使用大於 0x10_FFFF 的碼位)。

  • UTF-7

    一種 7 位元安全(非 8 位元)編碼,如果傳輸或儲存無法支援 8 位元,這會很有用。由 RFC 2152 定義。

非字元碼位

Unicode 中保留 66 個碼位作為「非字元碼位」。這些碼位都具有 未指定 (Cn) "一般類別",且永遠不會將任何字元指定給這些碼位。這些碼位包含 U+FDD0U+FDEF(含)之間的 32 個碼位,以及 34 個碼位

U+FFFE   U+FFFF
U+1FFFE  U+1FFFF
U+2FFFE  U+2FFFF
...
U+EFFFE  U+EFFFF
U+FFFFE  U+FFFFF
U+10FFFE U+10FFFF

在 Unicode 7.0 之前,非字元「禁止用於 Unicode 文字資料的開放交換」,因此處理這些串流的程式碼可以使用這些碼位作為可以與字元資料混合的哨兵,且永遠可以與該資料區分。(本文件新增上述和下一段的重點標示。)

Unicode 7.0 變更措辭,因此這些碼位「不建議用於 Unicode 文字資料的開放交換」。7.0 標準繼續說明

    「如果在開放交換中收到非字元,應用程式不需要以任何方式來詮釋它。然而,建議將其識別為非字元並採取適當的動作,例如以U+FFFD替換字元,以指出文字中的問題。建議不要直接從此類文字中刪除非字元碼點,因為刪除未詮釋的字元可能會造成潛在的安全問題。(請參閱第 3.2 節一致性需求中的 C7 一致性條款,以及Unicode 技術報告 #36「Unicode 安全考量」)。」

進行這項變更的原因是發現各種商業工具(例如編輯器或用於原始碼控制等工具)的撰寫方式,使得它們無法處理使用這些碼點的程式檔案,實際上幾乎完全禁止使用這些碼點!而這從來都不是我們的本意。它們一直都應該可以在應用程式中,或在配合的應用程式集中,隨意使用。

如果您撰寫的程式碼(例如編輯器)應能處理任何 Unicode 文字資料,那麼您不應自行使用這些碼點,而應允許它們在輸入中。如果您需要哨兵,它們應為非合法 Unicode。對於 UTF-8 資料,您可以使用位元組 0xC0 和 0xC1 作為哨兵,因為它們從未出現在格式良好的 UTF-8 中。(UTF-EBCDIC 有等效項)。您也可以將 Unicode 碼點儲存在整數變數中,並使用負值作為哨兵。

如果您沒有撰寫此類工具,那麼是否接受非字元作為輸入取決於您(儘管標準建議您不要這樣做)。如果您使用 Perl 進行嚴格的輸入串流檢查,這些碼點將繼續被禁止。這是為了維持向後相容性(否則可能會出現潛在的安全漏洞,因為假設在傳遞給它之前會過濾掉非字元的毫無戒心的應用程式現在可能會在沒有警告的情況下開始接收它們)。若要進行嚴格檢查,您可以使用層 :encoding('UTF-8')

如果嘗試輸出非字元,Perl 會繼續發出警告(使用警告類別 "nonchar",它是 "utf8" 的子類別)。

超越 Unicode 編碼點

Unicode 編碼點的最大值為 U+10FFFF,而 Unicode 僅定義對該編碼點以下的運算。但 Perl 可對平台上可用的最大允許有號數字編碼點進行運算。然而,除非使用寬鬆規則,否則 Perl 絕不會接受輸入串流中的這些編碼點,並會在輸出時發出警告(使用警告類別 "non_unicode",這是 "utf8" 的子類別)。

由於 Unicode 規則未定義在這些編碼點上,如果對它們執行 Unicode 定義的運算,Perl 會使用我們認為合理的規則,同時通常會使用 "non_unicode" 類別發出警告。例如,uc("\x{11_0000}") 會產生此類警告,並將輸入參數作為其結果傳回,因為 Perl 定義每個非 Unicode 編碼點的大寫為編碼點本身。(所有大小寫轉換運算,不只是大寫,都以這種方式運作。)

在正規表示式中,比對 Unicode 屬性 \p{}\P{} 結構與這些編碼點的情況並不明確,而且隨著我們累積經驗,這些處理方式也有所改變。

一種可能性是將任何與這些編碼點的比對視為未定義。但由於 Perl 沒有比對未定義的概念,因此會將其轉換為失敗或 FALSE。這幾乎是 Perl 從 v5.14(這些編碼點的使用變得普遍可靠)到 v5.18 所執行的動作,但並不完全相同。不同之處在於 Perl 將所有 \p{} 比對視為失敗,但將所有 \P{} 比對視為成功。

這樣做的一個問題是,在某些情況下會導致意外且令人困惑的結果

chr(0x110000) =~ \p{ASCII_Hex_Digit=True}      # Failed on <= v5.18
chr(0x110000) =~ \p{ASCII_Hex_Digit=False}     # Failed! on <= v5.18

也就是說,它將兩個比對都視為未定義,並將其轉換為 false(對每個比對發出警告)。第一個情況是預期的結果,但第二個情況可能會違背直覺:「既然它們是互補的,怎麼可能兩個都是 false?」另一個問題是,實作將許多 Unicode 屬性比對最佳化為已經存在的更簡單、更快速的運算,不會發出警告。我們選擇不放棄這些最佳化,因為它們有助於絕大多數的比對,只為了針對比對高於 Unicode 碼點的不太可能發生的事件產生警告。

由於這些問題,從 v5.20 開始,Perl 將非 Unicode 碼點視為典型的未指派 Unicode 字元,並據此進行比對。(注意:Unicode 有非典型的未指派碼點。例如,它有非字元碼點,以及在指派後注定要從右到左書寫的碼點,例如阿拉伯文和希伯來文。Perl 假設沒有任何非 Unicode 碼點具有非典型屬性。)

在大多數情況下,當比對高於 Unicode 碼點與 Unicode 屬性時,Perl 會發出警告,如果結果對 \p{}TRUE,對 \P{}FALSE。例如

chr(0x110000) =~ \p{ASCII_Hex_Digit=True}      # Fails, no warning
chr(0x110000) =~ \p{ASCII_Hex_Digit=False}     # Succeeds, with warning

在這兩個範例中,被比對的字元是非 Unicode,因此 Unicode 沒有定義它應該如何比對。它明顯不是 ASCII 十六進位數字,所以第一個範例明顯應該失敗,而且它確實失敗了,沒有警告。但第二個範例可能會有未定義的結果,因此為 FALSE。因此會為它發出警告。

因此,與早期的 Perl 相比,警告會在較少的案例中提出,而且僅在結果可能引起爭議時才會提出。事實證明,Perl 所做的任何最佳化(或未來可能做的任何最佳化)都不會導致跳過警告,因此它解決了 Perl 早期方法的兩個問題。受此變更影響的最常用屬性是 \p{Unassigned},它是 \p{General_Category=Unassigned} 的簡寫。從 v5.20 開始,所有非 Unicode 碼點都被視為 Unassigned。在較早的版本中,比對會失敗,因為結果被視為未定義。

唯一不會在可能應該提出警告時提出警告的地方是,如果最佳化導致甚至不會嘗試整個模式比對。例如,Perl 可能會找出,若要讓字串與某個正規表示式模式相符,字串必須包含子字串 "foobar"。在嘗試比對之前,Perl 可能會尋找該子字串,如果找不到,就會立即讓比對失敗,而不會實際嘗試;因此,即使字串包含 Unicode 以上的碼點,也不會產生任何警告。

對於大多數應用程式而言,這種行為比早期的 Perl 更符合「執行我的意思」。但對於需要嚴格符合 Unicode 的程式碼,它會偵測較少的問題。因此,有額外的操作模式可供此類程式碼使用。如果正規表示式模式是在 "non_unicode" 警告類別已設為致命錯誤的詞彙範圍內編譯,則會啟用此模式,例如透過

use warnings FATAL => "non_unicode"

(請參閱 warnings)。在此操作模式中,Perl 會對與非 Unicode 碼點的所有比對提出警告(不只是有爭議的比對),而且它會跳過可能導致不輸出警告的最佳化。(它目前仍不會在未嘗試比對時提出警告,例如上述的 "foobar" 範例。)

總而言之,Perl 現在通常會將非 Unicode 編碼點視為正規表示式比對的典型 Unicode 未指派編碼點,僅在結果可能引發爭議時才會提出警告。不過,如果此警告已被設為致命錯誤,則不會略過。

所有這些有一個例外。\p{All} 看起來像 Unicode 屬性,但它是 Perl 擴充功能,定義為對所有可能的編碼點(無論是否為 Unicode)為真,因此在與非 Unicode 編碼點進行比對時,永遠不會產生警告。(在 v5.20 之前,它是 \p{Any} 的完全同義詞,比對編碼點 00x10FFFF。)

Unicode 的安全性影響

首先,請閱讀 Unicode 安全性考量

此外,請注意以下事項

  • 格式錯誤的 UTF-8

    UTF-8 結構嚴謹,因此許多位元組組合都是無效的。過去,Perl 嘗試繼續處理並對無效組合做出一些解釋,但這可能會導致安全漏洞,因此現在,如果 Perl 核心需要處理無效組合,它將會提出致命錯誤,或將那些位元組替換為 Unicode 替換字元所形成的順序,Unicode 就是為了這個目的而建立它的。

    每個碼點可以用多個可能的語法有效 UTF-8 序列表示。早期,Unicode 和 Perl 都認為這些都是有效的,但現在,所有比最短可能的序列更長的序列都被認為是格式錯誤的。

    Unicode 認為許多碼點是非法的,或應避免使用。Perl 通常會接受它們,只要它們通過任何可能嘗試排除它們的輸入篩選器。這些已在上面討論過(請參閱 「Unicode 編碼」 中 UTF-16 下的「代理代碼」、「非字元碼點」「Unicode 碼點以外」)。

  • 如果您不習慣 Unicode,正規表達式模式比對可能會讓您感到驚訝。從 Perl 5.14 開始,有幾個模式修改器可用於控制這一點,稱為字元集修改器。詳細資訊說明於 perlre 中的「字元集修改器」

如其他地方所討論的,Perl 的一隻腳(兩個蹄子?)踏在兩個世界中:舊的 ASCII 和單位元組區域設定的世界,以及新的 Unicode 世界,必要時進行升級。如果您的舊程式碼沒有明確使用 Unicode,就不應自動切換到 Unicode。

EBCDIC 上的 Perl 中的 Unicode

EBCDIC 平台支援 Unicode。請參閱 perlebcdic

除非特別討論 ASCII 與 EBCDIC 問題,否則本文檔和其他地方對 UTF-8 編碼的引用應理解為在 EBCDIC 平台上表示 UTF-EBCDIC。請參閱 perlebcdic 中的「Unicode 和 UTF」

因為 UTF-EBCDIC 與 UTF-8 非常相似,所以差異大多對您隱藏;use utf8(而不是像 use utfebcdic)宣告腳本使用平台的 Unicode「原生」8 位元編碼。(對於 ":utf8" 層也是如此。)

地區設定

請參閱 perllocale 中的「Unicode 和 UTF-8」

Unicode 未發生時

在 Perl 中,仍有許多地方可以將 Unicode(使用某種編碼)作為引數提供,或作為結果接收,或兩者皆可,但並非如此,儘管 Perl 具有輸入和輸出 Unicode 的廣泛方式,以及一些其他「進入點」,例如 @ARGV 陣列(有時可以解釋為 UTF-8)。

以下為此類介面。另請參閱 "「Unicode 錯誤」"。對於所有這些介面,Perl 目前(截至 v5.16.0)僅假設位元組字串作為引數和結果,或如果使用了(已棄用的)encoding 實用程式,則假設為 UTF-8 字串。

Perl 不嘗試解決這些情況中 Unicode 角色的原因之一,是因為答案高度依賴於作業系統和檔案系統。例如,檔案名稱是否可以為 Unicode 以及使用哪種編碼,並不是一個完全可攜帶的概念。對於 qxsystem 也是如此:「命令列介面」(以及其中哪一個)將如何處理 Unicode?

  • chdirchmodchownchrootexeclinklstatmkdirrenamermdirstatsymlinktruncateunlinkutime-X

  • %ENV

  • glob(又稱 <*>

  • openopendirsysopen

  • qx(又稱反引號運算子)、system

  • readdirreadlink

「Unicode 錯誤」

「Unicode 錯誤」一詞已套用於 Latin-1 Supplement 區塊中的碼位不一致,亦即介於 128 到 255 之間。在未指定區域設定的情況下,與所有其他字元或碼位不同,這些字元可能會根據生效的規則而有截然不同的語意。(碼位高於 255 的字元會強制套用 Unicode 規則;而 ASCII 字元的規則在 ASCII 和 Unicode 規則下都是相同的。)

在 Unicode 規則下,這些上層 Latin1 字元會被解釋為 Unicode 碼位,這表示它們具有與 Latin-1 (ISO-8859-1) 和 C1 控制項相同的語意。

「ASCII 規則與 Unicode 規則」 中所述,在 ASCII 規則下,它們被視為未指派的字元。

這可能會導致意外的結果。例如,如果將高於 255 的碼位附加到字串,字串的語意可能會突然改變,這會將規則從 ASCII 變更為 Unicode。以下程式及其輸出為例

$ perl -le'
    no feature "unicode_strings";
    $s1 = "\xC2";
    $s2 = "\x{2660}";
    for ($s1, $s2, $s1.$s2) {
        print /\w/ || 0;
    }
'
0
0
1

如果 s1s2 中都沒有 \w,為什麼它們的串聯會有?

這種異常源於 Perl 嘗試不干擾未採用 Unicode 的舊程式,同時又希望無縫加入 Unicode 支援。但結果證明並非無縫。(順帶一提,你可以選擇在發生這種情況時收到警告。請參閱 encoding::warnings。)

use feature 'unicode_strings' 已於 Perl v5.12 中加入,以解決此問題。它會影響下列事項

  • 變更純量的字母大小寫,也就是使用 uc()ucfirst()lc()lcfirst(),或在雙引號內容中使用 \L\U\u\l,例如正規表示式替換。

    在 Perl 5.12.0 中啟用 unicode_strings 時,通常會使用 Unicode 規則。請參閱 perlfunc 中的「lc」,了解這如何與其他各種實用模組搭配運作的詳細資訊。

  • 使用不區分大小寫 (/i) 的正規表示式比對。

    從 Perl 5.14.0 開始,在 unicode_strings 範圍內編譯的正規表示式,即使在範圍外執行或編譯成較大的正規表示式,也會使用 Unicode 規則。

  • 在正規表示式中比對多個屬性。

    這些屬性為 \b(沒有大括弧)、\B(沒有大括弧)、\s\S\w\W,以及所有 Posix 字元類別,除了 [[:ascii:]]

    從 Perl 5.14.0 開始,在 unicode_strings 範圍內編譯的正規表示式,即使在範圍外執行或編譯成較大的正規表示式,也會使用 Unicode 規則。

  • quotemeta 或其內聯等效項 \Q 中。

    從 Perl 5.16.0 開始,在 "quotemeta" in perlfunc 中所述的 unicode_strings 範圍內使用一致的引號規則。在此之前,或在其範圍之外,UTF-8 編碼字串中沒有引號超過 127 的碼點,但在位元組編碼字串中,128-255 之間的碼點總是帶有引號。

  • ..範圍 運算子中。

    從 Perl 5.26.0 開始,字串上的範圍運算子在 unicode_strings 範圍內一致地處理其長度。在此之前,或在其範圍之外,它可能會產生字元長度超過右側的字串,其中右側佔用的位元組多於正確的範圍端點。

  • split 的特殊情況空白分割 中。

    從 Perl 5.28.0 開始,split 函數的模式指定為包含單一空白的字串,在 unicode_strings 範圍內一致地處理空白字元。在此之前,或在其範圍之外,根據 Unicode 規則但不是根據 ASCII 規則的空白字元在位元組編碼字串中出現時,會被視為欄位內容而不是欄位分隔符號。

您可以從上述內容中看到 unicode_strings 的效果在多個 Perl 版本中增加。(而 Perl 對 Unicode 的支援持續改善;最好使用最新的可用版本,以取得最完整且準確的結果。)請注意,如果您 use v5.12 或更高版本,將自動選擇 unicode_strings

對於早於上述 Perl,或當字串傳遞給 unicode_strings 範圍外的函數時,請參閱下一部分。

強制 Perl 中的 Unicode(或取消強制 Perl 中的 Unicode)

有時(請參閱 "Unicode 未發生時""「Unicode 錯誤」"),您可能會遇到需要將位元組字串強制轉換為 UTF-8 或反之亦然的情況。標準模組 Encode 可用於此目的,或低階呼叫 utf8::upgrade($bytestring)utf8::downgrade($utf8string[, FAIL_OK])

請注意,如果字串包含不符合位元組的字元,utf8::downgrade() 會失敗。

對已處於所需狀態的字串呼叫任一函式,皆為無作用。p>

"ASCII 規則與 Unicode 規則" 提供了使用 Unicode 規則建立字串的所有方法。

在 XS 中使用 Unicode

請參閱 "perlguts 中的 Unicode 支援",以了解 XS 層級的 Unicode 簡介,以及 "perlapi 中的 Unicode 支援",以了解 API 詳細資料。

駭入 Perl 以在較早的 Unicode 版本上執行(僅限非常認真的駭客)

Perl 預設內建最新支援的 Unicode 版本,但目標是讓你可以變更為使用任何較早的版本。不過,在 Perl v5.20 和 v5.22 中,最早可用的版本是 Unicode 5.1。Perl v5.18 和 v5.24 能夠處理所有較早的版本。

從 Unicode 網站 https://www.unicode.org 下載所需 Unicode 版本中的檔案。這些檔案應取代 Perl 原始碼樹中 lib/unicore 中現有的檔案。遵循該目錄中 README.perl 中的指示變更其中一些名稱,然後建置 perl(請參閱 INSTALL)。

從 perl-5.6.X 移植程式碼

從 5.8 開始的 Perl 具有與 5.6 不同的 Unicode 模型。在 5.6 中,程式設計師需要使用 utf8 pragma 來宣告特定範圍預期會處理 Unicode 資料,並且必須確保只有 Unicode 資料會傳遞到該範圍。如果你有在 5.6 中運作的程式碼,你將需要對你的程式碼進行下列部分調整。範例的撰寫方式讓程式碼會繼續在 5.6 中運作,因此你應該可以安全地嘗試這些範例。

  • 應讀取或寫入 UTF-8 的檔案處理

    if ($] > 5.008) {
      binmode $fh, ":encoding(UTF-8)";
    }
  • 將傳遞給某些擴充的純量

    無論是 Compress::ZlibApache::Request 或任何說明書中未提及 Unicode 的擴充功能,您都需要確定已移除 UTF8 標記。請注意,在撰寫本文時(2012 年 1 月),所述模組並未支援 UTF-8。請查看文件以驗證這是否仍然正確。

    if ($] > 5.008) {
      require Encode;
      $val = Encode::encode("UTF-8", $val); # make octets
    }
  • 我們從擴充功能取得的純量

    如果您相信純量以 UTF-8 回傳,您很可能會想要還原 UTF8 標記

    if ($] > 5.008) {
      require Encode;
      $val = Encode::decode("UTF-8", $val);
    }
  • 相同,如果您真的確定它是 UTF-8

    if ($] > 5.008) {
      require Encode;
      Encode::_utf8_on($val);
    }
  • DBI fetchrow_arrayfetchrow_hashref 的包裝函式

    當資料庫僅包含 UTF-8 時,包裝函式或方法是取代所有 fetchrow_arrayfetchrow_hashref 呼叫的便利方式。包裝函式也會讓您更容易適應資料庫驅動程式未來的強化功能。請注意,在撰寫本文時(2012 年 1 月),DBI 沒有標準化處理 UTF-8 資料的方式。請查看 DBI 文件 以驗證這是否仍然正確。

    sub fetchrow {
      # $what is one of fetchrow_{array,hashref}
      my($self, $sth, $what) = @_;
      if ($] < 5.008) {
        return $sth->$what;
      } else {
        require Encode;
        if (wantarray) {
          my @arr = $sth->$what;
          for (@arr) {
            defined && /[^\000-\177]/ && Encode::_utf8_on($_);
          }
          return @arr;
        } else {
          my $ret = $sth->$what;
          if (ref $ret) {
            for my $k (keys %$ret) {
              defined
              && /[^\000-\177]/
              && Encode::_utf8_on($_) for $ret->{$k};
            }
            return $ret;
          } else {
            defined && /[^\000-\177]/ && Encode::_utf8_on($_) for $ret;
            return $ret;
          }
        }
      }
    }
  • 您知道只能包含 ASCII 的大型純量

    僅包含 ASCII 並標記為 UTF-8 的純量有時會拖累您的程式。如果您發現這種情況,請移除 UTF8 標記

    utf8::downgrade($val) if $] > 5.008;

錯誤

另請參閱上方的 "「Unicode 錯誤」"

與擴充功能的互動

當 Perl 與擴充功能交換資料時,擴充功能應能夠了解 UTF8 標記並採取相應的動作。如果擴充功能無法辨識該標記,則擴充功能可能會傳回標記錯誤的資料。

因此,如果您使用 Unicode 資料,請在使用每個模組時查閱其文件,以了解是否有任何 Unicode 資料交換問題。如果文件完全沒有提到 Unicode,請懷疑最糟的情況,並查看原始碼以了解模組的實作方式。完全以 Perl 編寫的模組不應造成問題。直接或間接存取以其他程式語言編寫的程式碼的模組有風險。

對於受影響的功能,避免資料毀損的簡單策略是,永遠明確地編碼交換的資料。選擇您知道擴充功能可以處理的編碼。將傳遞給擴充功能的參數轉換為該編碼,並將結果從該編碼轉換回來。撰寫包裝函式,為您進行轉換,這樣當擴充功能趕上時,您稍後可以變更函式。

舉例來說,假設熱門的 Foo::Bar::escape_html 函式尚未處理 Unicode 資料。包裝函式會將參數轉換為原始 UTF-8,並將結果轉換回 Perl 的內部表示,如下所示

sub my_escape_html ($) {
    my($what) = shift;
    return unless defined $what;
    Encode::decode("UTF-8", Foo::Bar::escape_html(
                                 Encode::encode("UTF-8", $what)));
}

有時,當擴充功能不轉換資料,而只是儲存和擷取資料時,您將可以使用危險的 Encode::_utf8_on() 函式。假設熱門的 Foo::Bar 擴充功能以 C 語言撰寫,提供一個 param 方法,讓您可以根據這些原型儲存和擷取資料

$self->param($name, $value);            # set a scalar
$value = $self->param($name);           # retrieve a scalar

如果它尚未提供任何編碼支援,可以撰寫一個具有此類 param 方法的衍生類別

sub param {
  my($self,$name,$value) = @_;
  utf8::upgrade($name);     # make sure it is UTF-8 encoded
  if (defined $value) {
    utf8::upgrade($value);  # make sure it is UTF-8 encoded
    return $self->SUPER::param($name,$value);
  } else {
    my $ret = $self->SUPER::param($name);
    Encode::_utf8_on($ret); # we know, it is UTF-8 encoded
    return $ret;
  }
}

一些擴充功能提供資料輸入/輸出點的篩選器,例如 DB_File::filter_store_key 和家族。在擴充功能的文件中尋找此類篩選器;它們可以讓轉換為 Unicode 資料容易許多。

速度

某些函數在處理 UTF-8 編碼字串時比處理位元組編碼字串慢。所有需要跳過字元的函數,例如 length()substr()index(),或匹配正規表示式,當基礎資料是位元組編碼時,其運作速度可以大幅提升。

在 Perl 5.8.0 中,速度慢的情況通常相當明顯;在 Perl 5.8.1 中,引入了快取機制,改善了這個情況。一般來說,使用 UTF-8 編碼字串的運算仍然較慢。例如,已知 Unicode 屬性(字元類別),例如 \p{Nd},比其較簡單的對應項,例如 [0-9],慢很多(5-20 倍)(不過,與匹配 [0-9] 的 10 個 ASCII 字元相比,有數百個 Unicode 字元匹配 Nd)。

另請參閱

perlunitutperluniintroperlunipropsEncodeopenutf8bytesperlretut"${^UNICODE}" in perlvarhttps://www.unicode.org/reports/tr44)。