2022年8月5日

Unicode:フラグ "u" とクラス \p{...}

JavaScriptは文字列にUnicodeエンコーディングを使用します。ほとんどの文字は2バイトでエンコードされますが、最大65536文字まで表現できます。

この範囲は、すべての可能な文字をエンコードするには十分ではありません。そのため、一部のまれな文字は4バイトでエンコードされます。たとえば、𝒳(数学のX)や😄(笑顔)、一部の象形文字などです。

いくつかの文字のUnicode値を以下に示します

文字 Unicode Unicodeでのバイト数
a 0x0061 2
0x2248 2
𝒳 0x1d4b3 4
𝒴 0x1d4b4 4
😄 0x1f604 4

そのため、aのような文字は2バイトを占めますが、𝒳𝒴😄のコードはより長く、4バイトです。

ずっと昔、JavaScript言語が作成されたとき、Unicodeエンコーディングはよりシンプルでした。4バイト文字はありませんでした。そのため、一部の言語機能はそれらを正しく処理しません。

たとえば、lengthはここに2文字あると考えています

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

…しかし、実際には1文字だけですよね?問題は、lengthが4バイトを2つの2バイト文字として扱うことです。これは正しくありません。なぜなら、それらは一緒に考慮する必要があるからです(いわゆる「サロゲートペア」。文字列の記事で読むことができます)。

デフォルトでは、正規表現も4バイトの「長い文字」を2バイトのペアとして扱います。そして、文字列の場合と同様に、奇妙な結果につながる可能性があります。これは、集合と範囲[...]の記事で後ほど説明します。

文字列とは異なり、正規表現には、このような問題を修正するフラグuがあります。このフラグを使用すると、正規表現は4バイト文字を正しく処理します。また、Unicodeプロパティ検索が利用可能になります。次に説明します。

Unicodeプロパティ \p{…}

Unicodeのすべての文字には、多くのプロパティがあります。それらは、文字が属する「カテゴリ」を記述し、それに関するその他の情報を含んでいます。

たとえば、文字にLetterプロパティがある場合、その文字は(任意の言語の)アルファベットに属することを意味します。また、Numberプロパティは、それが数字であることを意味します。アラビア語または中国語などです。

\p{…}と記述されたプロパティを持つ文字を検索できます。\p{…}を使用するには、正規表現にフラグuが必要です。

たとえば、\p{Letter}は任意の言語の文字を表します。LLetterのエイリアスであるため、\p{L}も使用できます。ほとんどすべてのプロパティに短いエイリアスがあります。

以下の例では、英語、グルジア語、韓国語の3種類の文字が見つかります。

let str = "A ბ ㄱ";

alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ
alert( str.match(/\p{L}/g) ); // null (no matches, \p doesn't work without the flag "u")

主な文字カテゴリとそのサブカテゴリを以下に示します

  • 文字 L
    • 小文字 Ll
    • 修飾子 Lm
    • タイトルケース Lt
    • 大文字 Lu
    • その他 Lo
  • 数字 N
    • 10進数字 Nd
    • 文字数字 Nl
    • その他 No
  • 句読点 P
    • コネクタ Pc
    • ダッシュ Pd
    • 開始引用符 Pi
    • 終了引用符 Pf
    • 開き Ps
    • 閉じ Pe
    • その他 Po
  • マーク M (アクセントなど)
    • スペース結合 Mc
    • 囲み Me
    • 非スペース Mn
  • 記号 S
    • 通貨 Sc
    • 修飾子 Sk
    • 数学 Sm
    • その他 So
  • 区切り文字 Z
    • Zl
    • 段落 Zp
    • スペース Zs
  • その他 C
    • 制御 Cc
    • フォーマット Cf
    • 未割り当て Cn
    • 私用 Co
    • サロゲート Cs

そのため、たとえば小文字の文字が必要な場合は\p{Ll}、句読点の場合は\p{P}などと書くことができます。

他にも次のような派生カテゴリがあります。

  • Alphabetic (Alpha) には、文字 L と文字数字 Nl (例:Ⅻ - ローマ数字12の文字) とその他の記号 Other_Alphabetic (OAlpha) が含まれます。
  • Hex_Digit には、16進数字が含まれます:0-9a-f
  • …など。

Unicodeは多くの異なるプロパティをサポートしており、それらの完全なリストには多くのスペースが必要になるため、ここに参考文献を示します

例:16進数

たとえば、xFFのように記述された16進数を検索してみましょう。ここで、Fは16進数字(0…9またはA…F)です。

16進数字は\p{Hex_Digit}と表すことができます

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

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

例:漢字

漢字を探してみましょう。

Script(書記体系)というUnicodeプロパティがあり、値はCyrillicGreekArabicHan(中国語)などです。完全なリストはこちら

特定の書記体系の文字を検索するには、Script=<value>を使用する必要があります。たとえば、キリル文字の場合は\p{sc=Cyrillic}、漢字の場合は\p{sc=Han}などです。

let regexp = /\p{sc=Han}/gu; // returns Chinese hieroglyphs

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

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

例:通貨

$¥などの通貨を表す文字には、Unicodeプロパティ\p{Currency_Symbol}(短いエイリアス:\p{Sc})があります。

これを使用して、「通貨、その後に数字」の形式で価格を検索してみましょう

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

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

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

後で、量指定子 +, *, ? and {n}の記事で、多くの数字を含む数字を検索する方法を説明します。

まとめ

フラグuは、正規表現でのUnicodeのサポートを有効にします。

これは2つのことを意味します

  1. 4バイトの文字は、2つの2バイト文字ではなく、単一の文字として正しく処理されます。
  2. Unicodeプロパティは検索で使用できます:\p{…}

Unicodeプロパティを使用すると、特定の言語の単語、特殊文字(引用符、通貨)などを検索できます。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善の提案がある場合は、コメントする代わりに、GitHubのissueまたはプルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • 数語のコードを挿入するには、<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。