JavaScript は文字列に対し Unicode エンコーディング を使用します。ほとんどの文字は2バイトでエンコーディングされています。これは最大で 65536 文字表現できます。
この範囲は可能なすべての文字をエンコードするのには十分な大きさではありません。そのため、𝒳 (数学的な X)や 😄 (スマイル)のような一部のまれ文字は4バイトでエンコードされています
これは比較のためのUnicode値です:
| Character | Unicode | Bytes |
|---|---|---|
| a | 0x0061 |
2 |
| ≈ | 0x2248 |
2 |
| 𝒳 | 0x1d4b3 |
4 |
| 𝒴 | 0x1d4b4 |
4 |
| 😄 | 0x1f604 |
4 |
したがって、a や ≈ と言った文字が2バイトを占め、珍しいものは4バイトになります。
JavaScript が登場したころは、Unicodeエンコーディングはシンプルでした: 4バイト文字がありませんでした。そのため、依然として一部の言語機能はUnicodeを正しく処理しません。
alert('😄'.length); // 2
alert('𝒳'.length); // 2
…ですが、1文字にしか見えませんよね? ポイントは length は4バイトを2つの2バイト文字として扱うということです。それらは2つをあわせてでしか意味をなさない(いわゆる “サロゲートペア”)ため、正しくありません。これに関しては 文字列 で記述しています。
デフォルトでは、通常の正規表現も4バイトの “長い文字” を2バイトの文字のペアとして扱います。そして、文字列で起こったように、おかしな結果になる場合があります。これについては後ほど、 集合と範囲 [...] の記事で説明します。
文字列とは異なり、通常の正規表現はこのような問題を解決するフラグ u を持っています。このフラグがあれば、正規表現は4バイト文字を正しく扱うことができます。そしてUnicodeプロパティ検索も可能になります。次で説明していきます。
Unicodeプロパティ \p{…}
Unicodeのすべての文字には多くのプロパティがあります。それらは文字が “どのカテゴリ” に属しているかを説明し、それに関する様々な情報を含みます。
例えば、文字が Letter プロパティを持っている場合、その文字は(任意の言語の)アルファベットに属することを意味します。また、Number プロパティは数値であることを意味します: アラビア語や中国語など。
\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.
- lowercase
- Number
N:- decimal digit
Nd, - letter number
Nl, - other
No.
- decimal digit
- Punctuation
P:- connector
Pc, - dash
Pd, - initial quote
Pi, - final quote
Pf, - open
Ps, - close
Pe, - other
Po.
- connector
- Mark
M(accents etc):- spacing combining
Mc, - enclosing
Me, - non-spacing
Mn.
- spacing combining
- Symbol
S:- currency
Sc, - modifier
Sk, - math
Sm, - other
So.
- currency
- Separator
Z:- line
Zl, - paragraph
Zp, - space
Zs.
- line
- Other
C:- control
Cc, - format
Cf, - not assigned
Cn, – private useCo, - surrogate
Cs.
- control
なので、例えば小文字の文字が必要な場合は \p{Ll}、句読点(punctuation)が必要であれば \p{P} といったようにできます。
次のような派生カテゴリもあります:
Alphabetic(Alpha)は文字Lに加え、文字番号Nl(例: Ⅻ – ローマ数字の12)、一部の記号Other_Alphabetic(OAlpha)を含みます。Hex_Digitは16進数です:0-9,a-f。- などなど
Unicodeは多くの異なるプロパティをサポートしており、それらの完全なリストはここでは書ききれないため、ここではその参照を示します:
- 文字毎の全プロパティのリスト: https://unicode.org/cldr/utility/character.jsp.
- プロパティ毎の全文字のリスト: https://unicode.org/cldr/utility/list-unicodeset.jsp.
- プロパティの短縮エイリアス: https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt.
- すべてのプロパティを含むテキスト形式でのUnicode文字の完全なベースはここです: https://www.unicode.org/Public/UCD/latest/ucd/.
例: 16進数
例えば、xFF のようにして書かれた16進数を探しましょう。ここで F は16進数値です(0…1 or A…F)。
16進数は \p{Hex_Digit} で表すことができます。:
let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u;
alert("number: xAF".match(regexp)); // xAF
例: 中国の象形文字
象形文字を探しましょう。
Cyrillic, Greek, Arabic, Han(中国語)などの値をもつ、Unicodeプロパティ Script (書記体系)があります。完全なリストはこちらです.
指定された書記体系で文字を探すには、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} です。
“通貨に続いて数値” があるフォーマットに対して、価格を探していましょう。:
let regexp = /\p{Sc}\d/gu;
let str = `Prices: $2, €1, ¥9`;
alert( str.match(regexp) ); // $2,€1,¥9
後ほど、記事 量指定子 +, *, ? と {n} で多くの数字を含む数値の探し方を見ていきます。
サマリ
フラグ u は正規表現でのUnicodeサポートを有効にします。
これは2つのことを意味します:
- 4バイト文字が2つの2バイトの文字ではなく、1つの文字として正しく処理されます。
- 検索
\p{…}で Unicode プロパティが利用できます。
Unicode プロパティを利用すると、指定された言語の単語や特殊文字(引用符、通貨)などを探すことができます。
コメント
<code>タグを使ってください。複数行の場合は<pre>を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。