正規表現(Regex)とは、文字列の中の特定の要素を対象にして操作するために使える文字の並びです。これを使うと、次のことができます。
この概念では、JavaScriptで正規表現を使うことに焦点を当てます。正規表現の書き方そのものを学びたい場合は、Eloquent JavaScriptがよい資料になるでしょう。
💡 JavaScriptの正規表現の仕様は、この言語のECMA-262標準の一部です。つまり、ブラウザー固有の互換性を心配する必要はありません。
JavaScriptでは、正規表現は主に/pattern/modifiersという形式で書きます。ここでいう「pattern」は正規表現の本体で、「modifiers」は特別なオプション(いわば超能力)を指定するために付け加える文字の並びです。
とはいえ、正規表現を作る方法は2つあります。
const regex = /[a-z]/;
const regex = new RegExp('[a-z]');
どちらの場合も、JavaScriptは正規表現からオブジェクトを作ります。基本的には、リテラルで書いた不変のパターンを使うのがおすすめです。
RegExpコンストラクターは、正規表現が変化する場合や、まだ正規表現がわからない場合(入力など)に使えます。
正規表現には、flagsと呼ばれる任意の超能力があり、これによって追加の機能が使えます。これらのフラグは、単独でも、任意の順序で組み合わせても使え、正規表現の一部として含まれます。
よく使われるものには、次のようなものがあります。
/g - グローバル検索/i - 大文字と小文字を区別しない/m - 複数行検索簡単な例を見てみましょう。
const re = /home/gi;
const str = 'Home, sweet home.';
str.match(re);
// => ["Home", "home"]
gを付けると、文字列の中の一致するすべての箇所を調べられます。この機能がないと、JavaScriptは最初に見つかったHomeだけを抽出します。
大文字と小文字を区別しないフラグ/iを使うと、パターンを探すときに柔軟に対応できます。これにより、探しているものがUPPERCASEでもlowercaseでも関係ありません。
RegExpコンストラクターを使う場合、フラグの付け方の構文が異なります。この場合は、フラグを2番目の引数として渡すだけです。
正規表現をJavaScriptの組み込み関数と組み合わせると、文字列を操作したりデータを取り出したりする強力な方法が使えます。
test()メソッドは、ある文字列の中に目的の値が存在するかどうかを調べるのに最適です。結果は真偽値、つまりtrueかfalseで返ります。
match()メソッドは、ある文字列から正規表現に一致した部分を取り出します。一致した情報と結果をまとめた配列を返します。
どちらを使えばいいのでしょうか? 文字列の中に値があるかを手早く確認したいときはtest()、その値を使いたいときや一致に関する詳しい情報がほしいときはmatch()を使います。
test()メソッドは、正規表現と指定した文字列の間で一致するものを検索します。trueまたはfalseを返します。
const str = 'It is difficult to test if you have a virus';
const result = /virus$/.test(str);
console.log(result);
// => true
では、正規表現で検索した実際の値がほしいときはどうすればいいのでしょうか?
match()メソッドでは、単に真偽値を返す代わりに、見つかった一致があるかどうかによって中身が変わる便利な配列が得られます。
こうして、どんな文字列からでも情報を検索し抽出できます。たとえば、次のとおりです。
const funnyQuote =
'If you see someone crying, ask if it is because of their haircut.';
const regex1 = /someone/;
const regex2 = /happy/;
funnyQuote.match(regex1);
// => ["someone", index: 3, input: "If you see someone crying, ask if it is because of their haircut.", groups: undefined]
funnyQuote.match(regex2);
// => null
グローバル検索のフラグ/gがある場合は、インデックスや入力といった便利な情報を伴う1件の一致ではなく、配列の中にあるすべての出現箇所を返します。
const funnyQuote =
'If you see someone crying, ask if it is because of their haircut.';
const regex3 = /if/gi;
funnyQuote.match(regex3);
// => ["If", "if"];
JavaScriptのreplace()メソッドを使うと、ある文字列の中の値を検索し、新しい値に置き換えられます。
string.replace(searchValue, newValue);
置き換えの対象を探すパターンには、1つの文字列か、正規表現を使えます。
let string = 'I like dogs!';
let result = string.replace('dogs', 'cats');
let string = 'I would love to travel to Japan';
let result = string.replace(/Japan/g, 'Hawaii');
さらに、置き換え後の位置に関数を適用して、それぞれの値に追加の変更を加えることもできます。
let text = 'Say hello to the chatbot.';
let result = text.replace(/chatbot|hello/gi, function (word) {
return word.toUpperCase();
});
// => "Say HELLO to the CHATBOT"
💡 結局のところ、正規表現と
replace()メソッドを組み合わせると、要素をより柔軟に置き換えられます。1つの文字列を使う方法には限界があるかもしれません。
JavaScriptのsplit()メソッドは、正規表現を使って文字列を扱い、操作する別の方法です。
この方法では、パターンを認識してある文字列を分割するために正規表現を使います。たとえばstr.split(/[,.\s]/)です。このパターンはseparatorとして使われます。
const str = 'hello,user.how are.you';
const result = str.split(/[,.\s]/);
console.log(result);
// => ['hello', 'user', 'how', 'are', 'you']
パフォーマンスについて言うと、どちらもRegExpオブジェクトを作ります。主な違いは、正規表現がコンパイルされる頻度です。
Regular Expression Literalの場合: 最初にコードを解析してコンパイルするときに1回RegExp()構文の場合: 新しいオブジェクトが作られるたびに毎回リテラルの構文を使うほうが、パフォーマンスだけでなく、シンプルさと読みやすさの面でもよい選択かもしれません。詳しくは、こちらのStackoverflowのディスカッションを参照してください。
RegExp()コンストラクターを使うときは、引用符をエスケープし、バックスラッシュを二重にエスケープする必要もあります。そのため、もともと読みにくく理解しにくい正規表現が、さらに読みにくくなります。