角括弧 […] 内の複数の文字または文字クラスは “指定された中の任意の文字を探す” ことを意味します。
集合
例えば、[eao] は3文字 'a', 'e', または 'o' のいずれかを意味します。
それは 集合 と呼ばれます。集合は通常の文字と併せて正規表現の中で使うことができます。:
// [t or m], 次に "op" となる文字列を見つける
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
集合には複数の文字がありますが、マッチした中での1文字に相当することに注意してください。
従って、下の例ではマッチするものはありません:
// "V" に続き [o or i], その後 "la" となる文字列を見つける
alert( "Voila".match(/V[oi]la/) ); // null, マッチしない
パターンは次のように想定します:
V,- 次に文字
[oi]の 1つ, - 次に
la.
なので、Vola もしくは Vila がマッチします。
範囲
角括弧は 文字の範囲 を含むこともあります。
例えば、[a-z] は a から z までの範囲の文字で、 [0-5] は 0 から 5 までの数字です。
下の例では、x に続いて2桁の数字または A から F までの文字を探しています:
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
ここでは [0-9A-F] には2つの範囲があります: 0 から 9 の数値か、A から F の文字を探します。
小文字も見つけたければ、範囲 a-f を追加することもできます: [0-9A-Fa-f]。あるいはフラグ i を追加します。
また、[…] の中で文字クラスを使用することも可能です。
例えば、単語文字 \w やハイフン - を見つけたい場合は、[\w-] となります。
複数のクラスを連結することも可能です。例えば [\s\d] は “空白文字 or 数値” を意味します。
例:
- \d –
[0-9]と同じです, - \w –
[a-zA-Z0-9_]と同じです, - \s –
[\t\n\v\f\r ]に他のユニコードの空白文字を加えたものと同じです。
例: 多言語 \w
文字クラス \w は [a-zA-Z0-9_] の短縮形なので、中国語やキリル文字などを見つけることはできません。
任意の言語の文字を探す、よりユニバーサルなパターンを記述することができます。Unicode プロパティを使用すると簡単です: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}].
これを読み解いていきましょう。\w と同様、次の Unicode プロパティに沿った文字を含む、独自のセットを作成しています。:
Alphabetic(Alpha) – 文字,Mark(M) – アクセント e.g',~,Decimal_Number(Nd) – 数字,Connector_Punctuation(Pc) – アンダースコア'_'及び同様の文字,Join_Control(Join_C) – 2つの特別なコード200cと200d。アラビア語などで、合字で使われます。
使用例:
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hi 你好 12`;
// すべての文字と数字を探します
alert( str.match(regexp) ); // H,i,你,好,1,2
もちろんこのパターンは編集できます: Unicode プロパティを追加したり、削除するなど。Unicode プロパティに関する詳細は Unicode(ユニコード): フラグ "u" とクラス \p{...} で説明しています。
Unicode プロパティ p{…} はまだ Edge and Firefox では実装されていません。どうしても必要な場合は、ライブラリ XRegExp を利用してください。
あるいは、関心のある言語の文字範囲を利用します。例えば、キリル文字は [а-я] です。
範囲を除外する
通常の範囲に加えて、 [^…] のような、範囲を “除外” するものもあります。
それらは開始時にキャレット文字 ^ で指定され、指定されたもの以外の文字 と一致します。
例:
[^aeyo]–'a','e','y'または'o'を除く任意の文字.[^0-9]– 数字以外の任意の文字,\Dと同じです.[^\s]– 任意の非空白文字,\Sと同じです.
下の例では、文字、数字または空白以外の文字を探します:
alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and .
[…] でのエスケープ
通常、特別な文字そのままを見つけたい場合は、 \. のようにエスケープする必要があります。また、バックスラッシュが必要であれば \\ とします。
角括弧の中では、エスケープせずに大部分の特殊文字を使用することができます。:
- 記号
. + ( )はエスケープする必要はありません。 - ハイフン
-は先頭または末尾(範囲を定義しない場合)ではエスケープされません。 - キャレット
^は先頭(除外を意味します)でのみエスケープされます。 - 閉じ角括弧
]は常にエスケープされます(その記号を見つける必要がある場合)。
つまり、角括弧で何か意味するものを除き、すべての特別な文字はエスケープなしで利用できます。
角括弧内のドット . は単なるドットを意味します。パターン [.,] は、ドットあるいはカンマのいずれかの文字を探します。
以下の例では、正規表現 [-().^+] は -().^+ のいずれかの文字を探します。:
// No need to escape
let regexp = /[-().^+]/g;
alert( "1 + 2 - 3".match(regexp) ); // +, - にマッチします
…ただし、“念のため” にエスケープしたとしても害はありません。:
// 全部エスケープ
let regexp = /[\-\(\)\.\^\+]/g;
alert( "1 + 2 - 3".match(regexp) ); // 問題なく動作します: +, -
範囲とフラグ “u”
集合の中にサロゲートペアがある場合、正しく動作させるためにフラグ u が必要になります。
例えば、文字列 𝒳 で [𝒳𝒴] を探してみましょう。:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // [?] のような変な文字が表示されます
// (検索は正しく実行されず、文字の片方が返却されます)
結果は正しくありません。なぜなら、正規表現はデフォルトではサロゲートペアのことは “知らない” からです。
正規表現のエンジンは [𝒳𝒴] は2文字ではなく、4文字だと考えます:
𝒳の左半分(1),𝒳の右半分(2),𝒴の左半分(3),𝒴の右半分(4).
次のようにしてそれぞれのコードを確認することができます:
for(let i=0; i<'𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
したがって、上の例では 𝒳 の左半分を探して表示します。
フラグ u を追加すると、正しい振る舞いとなります。:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
[𝒳-𝒴] といった範囲を探す際にも同様の状況が起こります。
フラグ u を追加し忘れた場合、エラーになります。:
'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression
理由は、フラグ u がない場合、サロゲートペアは2文字として認識されるため、[𝒳-𝒴] は [<55349><56499>-<55349><56500>] (すべてのサロゲートペアがコードに置換される)と解釈されます。これで、範囲 56499-55349 が正しくないことが簡単にわかります: その開始コード 56499 は終わり 55349 よりも大きいです。これがエラーの正式な理由です。
フラグ u があると、パターンは正しく動作します。:
// 𝒳 から 𝒵 を探します
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴
コメント
<code>タグを使ってください。複数行の場合は<pre>を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。