角括弧 […]
内の複数の文字または文字クラスは “指定された中の任意の文字を探す” ことを意味します。
集合
例えば、[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) ); // 𝒴