iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
1
Modern Web

JavaScript 之旅系列 第 11

JavaScript 之旅 (11):RegExp Unicode property escapes

  • 分享至 

  • xImage
  •  

本篇介紹 ES2018 (ES9) 提供的 RegExp 的 Unicode property escapes。

本篇會有很多特殊字元,但 IT 鐵人這裡無法顯示這些字元,所以內文會看到大量的「???」XDD

過去的 RegExp

過去 ECMAScript 的原生 RegExp 無法直接存取有些 Unicode 的字元 property,簡化來說就是原生 RegExp 無法完整支援 Unicode。

因為原生未提供,所以只能透過其他 library 來解決,但這些方法不是很理想:

  • 使用類似 XRegExp 的 library 在 runtime 時建立 RegExp
    • 缺點:因是 runtime 的依賴,所以效能可能不是很理想。若在 web 上使用會增加載入時間。若 Unicode 更新標準時,需更新新版的 XRegExp 才能使用最新的可用資料
  • 使用類似 Regenerate 的 library 在 build time 時生成 RegExp
    • 缺點:需要 build script,若 Unicode 更新標準時,需自行更新 build script,且需部署最新的可用資料

詳情可參閱此提案

現今的 RegExp Unicode property escapes

在 ES2018 (ES9) 提供了 RegExp 的 Unicode property escapes,語法為 \p{...}\P{...},而其中的 p 代表的是「property」的意思。Unicode property escapes 是一種新的 escape sequence (跳脫序列),可用於設定 u flag 的 RegExp,這樣可以更好的表示 \p{...} 內的表達式與 Unicode property 有關。

此提案解決了過去的問題,因為已變為原生支援,無須使用額外的 library。

\P{...}\p{...} 的反向形式 (negated form),就跟 \d 代表數字字元,\D 代表非數字字元一樣,大寫字有「negated」的意思。

非 binary Unicode property 的 Unicode property escapes 語法如下:

\p{UnicodePropertyName=UnicodePropertyValue}

上面的語法中,可用 Unicode 的 PropertyAliases.txtPropertyValueAliases.txt 中定義的 alias 替換 ECMAScript spec 定義的 UnicodePropertyNameUnicodePropertyValue

若是 binary property 的 Unicode property escapes 語法如下:

\p{LoneUnicodePropertyNameOrValue}

此語法也可作為 General_Category 值的簡寫,例如:是 \p{Letter},而不是 \p{General_Category=Letter}

若使用未知的 property name 或 value 會觸發 early SyntaxError

支援 Unicode 的 \d\D 版本

若要 match Unicode 中的任何十進位數字,而不只是 match ASCII 的 [0-9],請使用 UTS18 中的 \p{Decimal_Number},而不是 \d

let decimalNumberPattern = /^\p{Decimal_Number}+$/u;
decimalNumberPattern.test('????????????????');
// true

若要不 match,請用 \P{Decimal_Number},而不是 \D

match Unicode 中的任何 numeric symbol

match Unicode 中的任何 numeric symbol,包括 non-decimal symbol,例如:羅馬數字:

let numericPattern = /^\p{Number}+$/u;
numericPattern.test('²³¹¼½¾????????????????㉛㉜㉝ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ');
// true

支援 Unicode 的 \w\W 版本

若要 match Unicode 中的任何 word symbol,而不只是 match ASCII 的 [a-zA-Z0-9_],請使用 UTS18 中的 [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}],而不是 \w

let wordSymbolPattern = /([\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]+)/gu;
let text = 'Macedonian: летачко';

for (match of text.matchAll(wordSymbolPattern)) {
  console.log(match);
}
// ["Macedonian", "Macedonian", index: 0, input: "Macedonian: летачко", groups: undefined]
// ["летачко", "летачко", index: 12, input: "Macedonian: летачко", groups: undefined]

之後會介紹到 String.prototype.matchAll()

若要不 match,請用 [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}],而不是 \W

match emoji

若要 match emoji,可用 UTR51 的 binary property。

例如:下面 pattern 可 match 以下內容:

  • 有可選 modifier 的 emoji:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?
  • 預設應為 emoji presentation,而不是 text presentation:\p{Emoji_Presentation}
  • 預設應為 text,但使用 U+FE0F VARIATION SELECTOR-16 會強制顯示為 emoji:\p{Emoji}\uFE0F
let emojiPattern = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu;
let text = `
  \u{231A}: ⌚ default emoji presentation character (Emoji_Presentation)
  \u{2194}\u{FE0F}: ↔️ default text presentation character rendered as emoji
  \u{1F466}: ? emoji modifier base (Emoji_Modifier_Base)
  \u{1F466}\u{1F3FB}: ?? emoji modifier base followed by a modifier
`;

for (match of text.matchAll(emojiPattern)) {
  const emoji = match[0];
  console.log(`${emoji} - code points: ${ [...emoji].length }`);
}
// ⌚ - code points: 1
// ⌚ - code points: 1
// ↔️ - code points: 2
// ↔️ - code points: 2
// ? - code points: 1
// ? - code points: 1
// ?? - code points: 2
// ?? - code points: 2

其中的 Emoji Modifiers 是在 Unicode 8.0 發布的,可用來設定人的 emoji 膚色。

只要將原本人的 emoji 加上膚色的 emoji modifier 就能變成不同膚色人的 emoji,例如:\u{1F466}\u{1F3FB}\u{1F466} 是人的 emoji,\u{1F3FB} 是 膚色的 emoji modifier。

不支援 loose matching

在 Unicode 的 spec 定義中有支援 loose matching 規則 UAX44-LM3,包括忽略大小寫、空格、_- 等。

例如:

  • \p{lB=Ba} 等同於 \p{Line_Break=Break_After}
  • /\p{___lower C-A-S-E___}/u 等同於 /\p{Lowercase}/u

但在此提案中提到,loose matching 會影響程式碼的可讀性和可維護性,所以才不支援 loose matching。

資料來源


上一篇
JavaScript 之旅 (10):RegExp Named Capture Groups
下一篇
JavaScript 之旅 (12):RegExp Lookbehind Assertions
系列文
JavaScript 之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言