Javascript での正規表現の宣言、オプション、使用方法についてまとめました。
目次
Javascript の正規表現の宣言
Javascript における正規表現は、「正規表現オブジェクト」を通して利用します。正規表現オブジェクトの生成方法は2種類あります。
- 「正規表現リテラル」を用いる
- コンストラクタ関数 RegExp() を使う
// 1)正規表現リテラルを利用する書き方
var re = /.+ラーメン(A|B)セット/;
// 2)コンストラクタ関数を利用する書き方
var re = new RegExp('.+ラーメン(A|B)セット');
(1)のリテラル表現のほうがややシンプルに見えますね。どちらでも好きな方を使えばOKです。それぞれの違いについては、この記事の下の方で、もうすこし取り上げています。
なんだかお腹が空いてきたよ〜。味噌ラーメンAセットで。
正規表現のオプションについて
あえて説明する必要はないかもしれませんが、オプション(フラグ)は、下記の方法で付与できます。
// リテラルの場合は末尾に
var re = /サッポロ一番/gm;
// コンストラクタ は第二引数に。
var re = new RegExp('サッポロ一番', 'gm' );
Javascript 正規表現のオプションの一覧です。特に「m (multi-line)」「s (single line)」「i (Case Insensitive)」などは、よく利用するかもしれません。
フラグ | デフォルト値 | 説明 |
---|---|---|
g | false | グローバルサーチ。RegExp.exec() や、String.match() の振る舞いが変化します。 |
i | false | 大文字・小文字を区別せず、マッチングします。 |
m | false | 複数行検索。^ や $ が改行の直後・直前にマッチするようになります。 |
s | false | .(ドット)が改行文字とも一致するようになります。 |
u | false | "unicode"; パターンをユニコードのコードポイントの連続として扱います。 |
y | false | 対象文字列で最後に見つかったマッチの位置から検索を開始する先頭固定 (sticky) 検索を行います。 |
それぞれ仕様について明確に知っておくと、スムーズに正規表現をかけるはずじゃよ。
Javascript で正規表現を実行する関数
宣言した正規表現オブジェクトを実行するには、2種類のアプローチがあります。
- 方法A:正規表現クラスから、オブジェクトを生成して、付随するメソッドを使う
- 方法B:Stringオブジェクトのマッチング関数を利用する
どちらでも、好きな方を利用できますが、それぞれ、置換やマッチングに関する関数が複数あります。それぞれ、見ていきましょう。
マッチ文字列を取得する
RegExp.exec()
Javascript の一般的な正規表現の実行関数です。引数に対象文字列を与えます。返り値は配列となっており、1番目に全体のマッチ、1番目以降にサブパターンのマッチが入ってきます。マッチが見つからない場合は null です。
var re = /.+(そば)/;
// マッチ
console.log( re.exec('たぬきそば') ); // ['たぬきそば', 'そば']
// マッチなし
console.log( re.exec('きつねうどん') ); // null
String.match()
文字列オブジェクトの関数、.match() メソッドです。引数は正規表現(Stringオブジェクトも可)、返り値は上記の RegExp.exec() と同じです(※)。
var subject = 'カレーうどん';
var result = subject.match(/カレー/);
console.log(result); // カレー
※マッチ文字列をすべて取得したい時・・・、
正規表現のオプション「g (Global)」を利用して、文字列中のすべてのマッチを見つけることができます。
上記2つのメソッドは、一見よく似ていますが、g オプションが付与された時に、返り値が変わります。
// g オプションを付与した正規表現
var re_g = /[^、]+?(パン)/g;
// コンストラクタを使う場合は、第二引数に渡す
var re_g = new RegExp('[^、]+?(パン)', 'g');
返り値の違いがわかりやすいように、対象文字列を長くします。「XXパン」を見つけます。
// 対象文字列
var subject = 'アンパンマン、カレーパンマン、天丼マン、ロールパンナ';
// 正規表現
var re = /[^、]+?(パン)/;
var re_g = /[^、]+?(パン)/g;
String.match() では、gオプションがあれば、返り値の配列の0番目から順に、マッチした文字列が格納されています。この場合、サブパターンのマッチは返り値に含まれません。
// String.match() の実行
subject.match(re); // ['アンパン', 'パン']
// String.match() の実行(gオプションあり)
subject.match(re_g); // ["アンパン", "カレーパン", "ロールパン"]
一方で、RegExp.exec() 関数を実行すると、一見返り値は変わりませんが、実行のたびに正規表現オブジェクトの lastIndex プロパティが、直前の実行でマッチした文字列の直後のインデックスに更新されるようになります。
RegExp.exec() 関数は、lastIndex 直後から、マッチングを始めるように振る舞います。
// RegExp.exec() の実行
re.exec(subject); // ['アンパン', 'パン']
console.log( re.lastIndex ); // 0
re.exec(subject); // ['アンパン', 'パン']
console.log( re.lastIndex ); // 0, ずっと0のまま
// RegExp.exec() の実行 (g あり)
re.exec(subject); // ['アンパン', 'パン']
console.log( re_g.lastIndex ); // 4,
re.exec(subject); // ['カレーパン', 'パン']
console.log( re_g.lastIndex ); // 12,
re.exec(subject); // ['ロールパン', 'パン']
console.log( re_g.lastIndex ); // 20
re.exec(subject); // null
console.log( re_g.lastIndex ); // 0
これを利用したループ処理で、繰り返しマッチを行うことができます。
// ループして実行
while ( (result = re.exec(subject)) !== null) {
console.log( 'マッチ:' + result[0] + ', lastIndex:' + re_g.lastIndex );
}
// 出力
// マッチ:アンパン, lastIndex:4
// マッチ:カレーパン, lastIndex:12
// マッチ:ロールパン, lastIndex:25
なんだか、僕は、String.match() のほうが、シンプルで使いやすそうな気がするよ〜。
マッチが見つかるかどうかチェックする
単にマッチするかどうかを知りたい時は、RegExpの.test() メソッドか。Stringの.search() が利用できます。
String.search()
Stringの.search() はマッチしたのインデックスを整数で返します。
search メソッドの返り値が、「0」の場合、真偽値に変換したときに、マッチしたにもかかわらず「false」と判定されてしまいますので、注意が必要です。マッチの判定をしたい場合、明示的に「-1 でない」ことを確かめる必要があります。
var result = 'javascript'.search(/java/);
console.log(result); // 0
if ( result !== -1 ) {
console.log('マッチしました!');
}
引数は、正規表現オブジェクトでなく、文字列でもOKです。
RegExp.test()
RegExpの.test() を使うと・・・、シンプルに、真偽値を返してくれます。単純にマッチかどうかを知りたければ、test() を使うとよいでしょう。
var result = /(java)script/.test('javascript');
console.log(result); // true
例4マッチ文字列を置換する
マッチングを置換するには、Stringのreplaceを使います。戻り値として新しい文字列オブジェクトが返されます。(もとの文字列は変更されません)
String.replace()
var subject = 'Javascript is flexible.';
// 単純な置換。iは大文字小文字を無視するオプション。
var result = subject.replace('/java/i', 'Action' );
console.log(result); // Actionscript is flexible.
// カッコ内のサブパターンを後方参照
var result = subject.replace('/(Java).*(easy)/', '$1 is not so $2.' );
console.log(result); // Java is not so flexible.
// グローバルな置換。最初の置換で処理が止まらず、何度でも置換する
var result = subject.replace('/o/g', 'O' );
console.log(result); // Java is nOt sO flexible.
RegExpを使って置換もできますが、上記 String 関数は幾分シンプルに記載できるでしょう。
【参考】正規表現の関数まとめ
RegExp オブジェクトの関数
メソッド | 説明 | 返り値(マッチした場合) | 返り値(マッチしない場合) |
---|---|---|---|
RegExp.exec(String) | 文字列中で一致するものを検索する | マッチの配列。0番目に全体のマッチ。1番目以降にサブグループのマッチ | null |
RegExp.test(String) | 文字列中で一致するものがあるかをテストする。 | true | false |
String オブジェクトの関数
result | 説明 | 返り値(マッチした場合) | 返り値(マッチしない場合) |
---|---|---|---|
String.match( RegExp ) | 結果情報の配列を返します。 | マッチの配列。0番目が全体のマッチ、1番目以降にサブパターンのマッチ | null |
String.search( RegExp ) | マッチした場所のインデックスを返します。 | インデックス(0以上の整数) | -1 |
String.replace( RegExp, 'xxxx' ) | マッチした部分文字列を別の部分文字列に置換する。 | 置換された文字列 | そのままの文字列 |
【参考】Javascript 正規表現コラム
正規表現リテラルとコンストラクタの違い
冒頭の正規表現の宣言をもう一度見てみましょう。
// (1)正規表現リテラルを利用する書き方
var re = /https?:\/\/example\.com/i;
// (2)コンストラクタ関数を利用する書き方
var re = new RegExp('https?://example\.com', 'i');
この2つの根本的な違いとして、(1)の正規表現リテラルは、Javascriptのロードされるタイミングでコンパイルされますが、一方で(2)コンストラクタに文字列を引数で与える場合、その宣言が実行されるタイミングでコンパイルされる、という違いがあります。
コンストラクタ関数を使用している時点で、正規表現に限った話ではないのですが、ループなどで全く同じ正規表現のを何回も実行するのであれば、繰り返しコンパイルしてしまうことでパフォーマンスに影響する点には、少し注意が必要です。
Javascript におけるデリミタ
(1)のように正規表現リテラルでは、「/」で囲まれることで、正規表現とみなされます。(「/」以外はデリミタとして認められません)。
この記事の冒頭でも触れましたが、デリミタとしてのスラッシュを使っていますので、上記のように正規表現自体ではスラッシュをエスケープする必要が生じますが、逆に(1)の正規表現宣言では、スラッシュをエスケープする必要はありません。
逆に、URL文字列など、「/」が多い正規表現では、リテラルだとエスケープが多くてちょっと読みにくくなるね。使い分けよう。
ちなみに、コンストラクタに直接、正規表現オブジェクトを引数で渡すこともできます。こちらも、Javascirpt ロード時に、コンパイルされる意味では、(1)と同じです。あえてこの宣言を使う事は、あまりないかもしれません。
// コンストラクタに直接正規表現オブジェクトを渡す
var re = new RegExp('/https?:\/\/example\.com/');
参考:「リテラル」とは
初学者にとって、「リテラル(Literal)」とはあまり聞き慣れない言葉かもしれません。リテラルとして表記できる型はプログラミング言語によりますが、例えば、Javascriptにおいて、下記はすべてリテラルによるオブジェクトの生成です。var num = 1; // Integer型のリテラル表現
var str = 'javascript'; // String型のリテラル表現
var str = [1, 2, 3]; // Array型のリテラル表現
var re = /javascript/; // RegExp型のリテラル表現
参考リンク
基本的な正規表現に関しては、こちらもご一読下さい。
また、正規表現のメタ文字に関しては、こちらにまとめています。
MDNのドキュメントです。詳しく記載されています。
リテラルで正規表現を宣言したほうが、スッキリかけるかもしれんな。