Javascript Unicode:修飾符 "u" 和類 \p{...}

2023-02-17 11:00 更新

JavaScript 對字符串使用 Unicode 編碼。大多數(shù)字符使用 2 個字節(jié)編碼,但這種方式只能編碼最多 65536 個字符。

這個范圍不足以對所有可能的字符進行編碼,這就是為什么使用 4 個字節(jié)對一些罕見的字符進行編碼,比如 (數(shù)學符號 X)或 (笑臉),一些象形文字等等。

下面是一些字符對應的 Unicode 值:

字符 Unicode Unicode 中的字節(jié)數(shù)
a 0x0061 2
0x2248 2
0x1d4b3 4
0x1d4b4 4
0x1f604 4

所以像 a 和  這樣的字符占用 2 個字節(jié),而 , 和  的對應編碼則更長,占用 4 個字節(jié)。

很久以前,當 JavaScript 被發(fā)明出來的時候,Unicode 編碼要更加簡單:當時沒有 4 個字節(jié)的字符。所以,有些語言功能現(xiàn)在仍無法正確處理它們。

比如 length 認為這里有 2 個字符:

alert(''.length); // 2
alert(''.length); // 2

……但我們可以清楚地認識到這里只有一個字符,對吧?關(guān)鍵在于 length 把 4 個字節(jié)當成了 2 個 2 字節(jié)長的字符。這是不對的,因為它們必須被當作一個整體來考慮(即所謂的“代理對(surrogate pair)”,你可以在 字符串 中閱讀關(guān)于代理對的更多信息)。

默認情況下,正則表達式也會把一個 4 個字節(jié)的“長字符”當成一對 2 個字節(jié)長的字符。正如在字符串中遇到的情況,這將導致一些奇怪的結(jié)果。我們很快會在后面的章節(jié) 集合和范圍 [...] 中遇到。

與字符串有所不同的是,正則表達式有一個修飾符 u 被用以解決此類問題。當一個正則表達式帶有這個修飾符后,4 個字節(jié)長的字符將被正確地處理。同時也能夠使用 Unicode 屬性進行查找了,我們接下來就一起學習它吧。

Unicode 屬性 \p{…}

Unicode 中的每個字符都有很多屬性。它們描述了字符所屬的“類別”,包含了關(guān)于字符的各種信息。

例如,如果一個字符具有 Letter 屬性,這意味著這個字符歸屬于(任意語言的)字母表。而 Number 屬性則表示這是一個數(shù)字:也許是阿拉伯數(shù)字,亦或是中文數(shù)字,等等。

我們可以查找具有某種屬性的字符,寫作 \p{…}。為了使用 \p{…},一個正則表達式必須使用修飾符 u

舉個例子,\p{Letter} 表示任何語言中的一個字母。我們也可以使用 \p{L},因為 L 是 Letter 的一個別名。對于每種屬性而言,幾乎都存在對應的縮寫別名。

在下面的例子中會找出來 3 種字母:英語、格魯吉亞語和韓語。

let str = "A ? ?";

alert( str.match(/\p{L}/gu) ); // A,?,?
alert( str.match(/\p{L}/g) ); // null(沒有匹配項,因為沒有修飾符 "u")

以下是主要的字符類別和它們對應的子類別:

  • 字母(Letter)L
    • 小寫(lowercase)Ll,
    • 修飾(modifier)Lm,
    • 首字母大寫(titlecase)Lt
    • 大寫(uppercase)Lu,
    • 其它(other)Lo。
  • 數(shù)字(Number)N
    • 十進制數(shù)字(decimal digit)Nd
    • 字母數(shù)字(letter number)Nl,
    • 其它(other)No
  • 標點符號(Punctuation)P
    • 連接符(connector)Pc,
    • 橫杠(dash)Pd
    • 起始引號(initial quote)Pi
    • 結(jié)束引號(final quote)Pf,
    • 開(open)Ps
    • 閉(close)Pe,
    • 其它(other)Po。
  • 標記(Mark)M(accents etc):
    • 間隔合并(spacing combining)Mc,
    • 封閉(enclosing)Me
    • 非間隔(non-spacing)Mn。
  • 符號(Symbol)S
    • 貨幣(currency)Sc,
    • 修飾(modifier)Sk,
    • 數(shù)學(math)Sm,
    • 其它(other)So。
  • 分隔符(Separator)Z
    • 行(line)Zl,
    • 段落(paragraph)Zp,
    • 空格(space)Zs。
  • 其它(Other)C
    • 控制符(control)Cc,
    • 格式(format)Cf,
    • 未分配(not assigned)Cn
    • 私有(private use)Co,
    • 代理偽字符(surrogate)Cs。

因此,比如說我們需要小寫的字母,就可以寫成 \p{Ll},標點符號寫作 \p{P} 等等。

也有其它派生的類別,例如:

  • AlphabeticAlpha),包含了字母 L,加上字母數(shù)字 Nl(例如 Ⅻ —— 羅馬數(shù)字 12),加上一些其它符號 Other_AlphabeticOAlpha)。
  • Hex_Digit 包括 16 進制數(shù)字 0-9,a-f
  • ……等等。

Unicode 支持很多不同的屬性,列出整個清單需要占用大量的篇幅,因此在這里列出相關(guān)的鏈接:

舉例:16 進制數(shù)字

舉個例子,讓我們來查找 16 進制數(shù)字,寫作 xFF 其中 F 是一個 16 進制的數(shù)字(0…9 或者 A…F)。

一個 16 進制數(shù)字可以表示為 \p{Hex_Digit}

let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u;

alert("number: xAF".match(regexp)); // xAF

舉例:中文字符

讓我們再來查找中文字符。

有一個 Unicode 屬性 Script(一個書寫系統(tǒng)),這個屬性可能有一個值:Cyrillic、Greek、Arabic、Han(中文)等等,這里是一個完整的列表。

要在給定的書寫系統(tǒng)中查找字符,我們需要使用 Script=<value>,例如對于西里爾字母:\p{sc=Cyrillic},中文象形文字:\p{sc=Han},等等。

let regexp = /\p{sc=Han}/gu; // 返回中文象形文字

let str = `Hello Привет 你好 123_456`;

alert( str.match(regexp) ); // 你,好

舉例:貨幣

表示貨幣的字符,例如 $、 和 ,具有 Unicode 屬性 \p{Currency_Symbol},縮寫為 \p{Sc}。

讓我們用它來查找格式為“貨幣,接著是一個數(shù)字”的價格:

let regexp = /\p{Sc}\d/gu;

let str = `Prices: $2, €1, ¥9`;

alert( str.match(regexp) ); // $2,€1,¥9

稍后,在文章 量詞 +, *, ? 和 {n} 中我們將學習如何查找包含很多位的數(shù)字。

總結(jié)

修飾符 u 表示啟用正則表達式中對 Unicode 的支持。

這意味著兩件事:

  1. 4 個字節(jié)長的字符被以正確的方式處理:被看成單個字符,而不是 2 個 2 字節(jié)長的字符。
  2. Unicode 屬性可以被用于查找:?\p{…}?。

有了 unicode 屬性我們可以查找給定語言中的詞,特殊字符(引用,貨幣)等等。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號