第15話:正規表現
目次
- マンガ
- マンガ台詞
- 説明
- 『正規表現』による『置換』
- 『正規表現』の『検索文字列』
- 『正規表現』の『フラグ』
- 『無名関数』を使った『置換』
- 『正規表現』オブジェクト
- 『正規表現』による検索
- サンプルの入手
マンガ
マンガ台詞
説明
この章では、『正規表現』について学びます。『正規表現』は、文字列の検索や置換を行うための、特殊なルールです。この『正規表現』はJavaScriptだけでなく、多くのプログラミング言語や、検索ソフトでも利用されています。
『正規表現』はJavaScriptだけでなく、多くの場面で応用の利く知識です。『正規表現』をマスターすると、コンピュータをより高度に活用できるようになります。
それでは以下、『正規表現』について、詳しく解説していきます。
『正規表現』による『置換』
『正規表現』による『置換』を行う際は、検索する文字列と、置換する文字列を、以下のように書きます。
【正規表現による置換】 文字列オブジェクト.replace(/【検索する文字列】/【フラグ】,【 置換する文字列】)
上記は、文字列オブジェクトの、『replace』メソッドを利用して、文字列の置換を行う際の書式です(文字列もオブジェクトの一種で、プロパティやメソッドを持っています)。
『replace』メソッドでは、最初の引数に『検索文字列』と『フラグ』を、2番目の引数に『置換文字列』を書きます。この『検索文字列』と『フラグ』の部分(『/【検索する文字列】/【フラグ】』)が『正規表現』です。
【正規表現の部分】 /【検索する文字列】/【フラグ】
この『検索文字列』『フラグ』『置換文字列』には、実際には、以下のような内容を記入します。
記入領域 | 実際に書き込む内容 | 例 |
---|---|---|
検索文字列 | 『文字列』と『正規表現の記号』を使った 検索の条件を表す文字列 |
内木守 |
フラグ | 検索の方法を制御する記号 | g |
置換文字列 | 検索結果を、置き換える文字列 | 安見遊 |
上記の表の『例』の部分を、プログラムとして書いてみましょう。
var a = "『吾輩は猫である』の感想 内木守"; // 置換前の文字列 var b = a.replace(/内木守/g, "安見遊"); // 置換 alert(b); // 結果を表示
【結果】
『吾輩は猫である』の感想 安見遊
『内木守』という文字列を検索して、『安見遊』という文字列に置き換えるプログラムです。この『replace』メソッドの引数に書いてある、『フラグ』の『g』は、『全て置換』という意味の記号です。
そのため上記のプログラムは、検索文字列『内木守』、フラグ『g』、置換文字列『安見遊』で、「文章中にある『内木守』という文字列を、『全て』『安見遊』に置換する」という処理になります。
『/』のエスケープ
さて、検索文字列は『/~/』のようにして、2つの文字で囲います。『/~/』の間に『/』を書きたい場合は、『\/』と書きます。
var a = "https://crocro.com/"; // 置換前の文字列 var b = a.replace(/https:\//g, "file:/"); // 置換。検索文字列内の『/』は、『\/』と書く alert(b); // 結果を表示
【結果】
file://crocro.com/
検索結果の取り出しと再利用
『検索文字列』中の『(~)』で囲んだ場所は、後で取り出して再利用できます。
正規表現の中で使用する際は、最初の『(~)』は『』という記号で、2番目の『(~)』は『』という記号で、3番目の『(~)』は『』という記号で……というように、『\』のあとに何番目の『(~)』かの『数字』を書いて、取り出すことができます。
以下の例では、『英数字で構成された文字列 (\w+?)』が、半角スペース後に『もう一度登場したら 』、『重複』と置換しています(『\w』は英数字を表す正規表現の記号)。
var namae = "dog cat cat monkey rat rat cow"; // 置換前の文字列
namae = namae.replace(/(\w+) /g, "重複"); // 置換
alert(namae); // 結果を表示
【結果】
dog 重複 monkey 重複 cow
また、『検索文字列』中の『(~)』で囲んだ場所は、置換用の文字列としても再利用できます。実際には、こちらの用途の方が多いと思います。
その際、最初の『(~)』は『』という記号で、2番目の『(~)』は『』という記号で、3番目の『(~)』は『』という記号で……というように、『$』のあとに何番目の『(~)』かの『数字』を書いて、取り出すことができます。
以下、実際に検索結果を取り出して再利用する例です。
var bunsyou = "安見遊は内木守より強い"; // 置換前の文字列 bunsyou = bunsyou.replace(/(.+)は(.+)より/g, "はより"); // 置換 alert(bunsyou); // 結果を表示
【結果】
内木守は安見遊より強い
この場合、最初の『(.+)』に一致する文字列は『安見遊』、2番目の『(.+)』に一致する文字列は『内木守』となります。この検索で『(~)』部分に一致した2つの文字列は『』(最初の括弧内の文字列)、『』(2番目の括弧内の文字列)として取り出して、『置換文字列』内で再利用できます。
上記プログラムでは、『はより』というように、『(.+)』に一致した部分の順番(『』『』)を、『』『』と入れ替えることで、名前を入れ替えた文字列を作成しています。
『正規表現』の『検索文字列』
『正規表現』の『検索文字列』には、様々な記号や書式が使えます。検索に使える記号や書式は非常に多いです。その一部を以下に示します。
正規表現でよく使う記号や書式:
書き方 | 意味 |
---|---|
A+ | 1個以上のA(可能な限り長く) |
A* | 0個以上のA(可能な限り長く) |
A+? | 1個以上のA(可能な限り短く) |
A*? | 0個以上のA(可能な限り短く) |
A? | 0または1個のA |
A{3} | 3個のA |
A{3,} | 3個以上のA |
A{3,5} | 3~5個のA |
A|B | AまたはB |
ABC|DEF | ABCまたはDEF |
[ABC] | A,B,Cのいずれか1文字 |
[A-C] | A~Cのいずれか1文字 |
[^ABC] | A,B,C以外のいずれか1文字 |
. | 任意の1文字 |
.+ | 任意の1以上の文字 |
^A | Aで始まる文字列 |
A$ | Aで終わる文字列 |
A.+B | 『A(任意の文字列)B』(可能な限り長く) |
A.+?B | 『A(任意の文字列)B』(可能な限り短く) |
A[^\/]+B | 『A(『/』を含まない文字列)B』 |
以下、マンガ中で出てきた検索内容を、実際にプログラムとして書いて解説します。
実例1 『.』(ドット)を使った置換
まずは最初の例です。『.』(ドット)を使った検索です。
var a = "abcd axcy a1c2"; // 置換前の文字列
var b = a.replace(/a.c/g, "●"); // 置換
document.write(b); // 結果を表示
【結果】
●d ●y ●2
この例では、『a.c』を検索文字列として設定しています。『.』(ドット)は、どんな文字でもよい、任意の1文字を表す記号です。そのため『a.c』は、『a』『何でもよい1文字』『c』という文字を表すことになります。つまり『a』と『c』の間に、1文字だけ何かが入っている部分は、全て一致することになります。
上記の例では『abcd axcy a1c2』のうち、『abc』『axc』『a1c』という部分が一致します。
実例2 『[~]』を使った置換
次は『[~]』を使った検索です。これは『[bx]』などのように、角括弧の中にいくつかの文字を入れて使用します。『[bx]』と書いた場合は『bかxの文字1つ分』という意味になります。
それでは『a[bx]c』という検索文字列を設定して、置換を行ってみます。
var a = "abcd axcy a1c2"; // 置換前の文字列
var b = a.replace(/a[bx]c/g, "●"); // 置換
document.write(b); // 結果を表示
【結果】
●d ●y a1c2
『a』『bかxの1文字』『c』という条件に一致する『abc』と『axc』が置換されて『●』に置き換わります。
実例3 『[~]』を使った置換2
この『[~]』を使った検索では、『[abcde]』と1文字ずつ書く以外にも、文字列の範囲を指定することができます。『[a-z]』とすれば、小文字の『a』から『z』までの文字全てとなります。また、『[a-dxyz]』のように、『a』から『d』の範囲と『x』『y』『z』、といった指定を行うことも可能です。
また、後述の『正規表現でよく使う記号』を混ぜて『[a-z\t\n]』のように書くこともできます。この場合は、『a』から『z』の範囲の文字全てと『タブ文字』と『改行』の、いずれかに該当する1文字となります。
以下、範囲を指定した検索文字列の指定の例です。
var a = "abcd axcy a1c2"; // 置換前の文字列
var b = a.replace(/a[a-f]c/g, "●"); // 置換
document.write(b); // 結果を表示
【結果】
●d axcy a1c2
『a』『aからfの範囲の1文字』『c』という条件に一致する、『abc』が置換されて『●』に置き換わります。
実例4 『[^~]』を使った置換
『[~]』を使った検索では、『条件に一致する任意の1文字』を指定していました。しかし時には『条件に一致しない任意の1文字』を検索対象にしたい場合もあるでしょう。そういった場合に利用するのが『[^~]』という書き方です。
『[^~]』は、角括弧内の文字列に一致しない任意の1文字を指定する書き方です。『[^bx]』とすれば、『b』『x』以外の何か1文字という意味になります。
以下、『[^~]』を利用した置換の例です。
var a = "abcd axcy a1c2"; // 置換前の文字列
var b = a.replace(/a[^bx]c/g, "●"); // 置換
document.write(b); // 結果を表示
【結果】
abcd axcy ●2
『a』『bでもxでもない1文字』『c』という条件に一致する、『a1c』が置換されて『●』に置き換わります。
『正規表現』の記号のエスケープ
『正規表現』の記号を、記号ではなく文字列として扱いたい場合は、文字の直前に『\』を付けます。
var a = "本[book]"; // 置換前の文字列 var b = a.replace(/\[book\]/g, ""); // 置換 document.write(b); // 結果を表示
【結果】
本
『\[』『\]』というように、『[』『]』の文字列を、『\』記号でエスケープしています。
上記のプログラムの場合、『[book]』という文字列がそのまま検索対象になります。そして検索文字列が、置換文字列『""』(空の文字列)で、置き換わります。そして、置換の結果残った文字列『本』が、変数『b』に入ります。
また、『\』自身を指定する場合は『\\』と書きます。
var a = "directory\\file.txt"; // 置換前の文字列
// 文字列中の『\』は、実際には『\』に変換されて使用される
var b = a.replace(/^.+\\/g, ""); // 置換
document.write(b); // 結果を表示
【結果】
file.txt
この『\』を『\\』と書く方法は、『正規表現』以外の文字列でも使用します。最初の行で『directory\\file.txt』と書いてある部分は、実際には『directory\file.txt』として、プログラム内では扱われます。
上記の検索文字列『^.+\\』の『.+』の部分は、「1文字以上の任意の文字列」となります。そのため、先頭(先頭を表す記号『^』)から『\』まで全てが、検索対象となります。
上記のプログラムは、先頭から『\』までの範囲の文字列を検索文字列として、置換文字列『""』(空の文字列)で置き換えるという処理です。結果として、『\』までの部分が全て削除された文字列『file.txt』が、変数『b』に入ります。
正規表現の、その他の記号
正規表現でよく使う記号
記号 | 意味 |
---|---|
\d | 数値([0-9]と同じ) |
\D | 数値以外([^0-9]と同じ) |
\w | 英数([A-Za-z0-9_]と同じ) |
\W | 英数以外([^A-Za-z0-9_]と同じ) |
\n | 改行 |
\r | 復帰 |
\t | タブ文字 |
\s | スペース、タブ、改ページ、改行を含む、1つの空白文字 |
\S | \s以外の文字 |
正規表現で知っていると役立つ書式(グループ化)
書き方 | 意味 |
---|---|
(?:aaa) | 『aaa』に一致するが、後で『$番号』として取り出さない |
aaa(?=bbb) | 『aaa』に『bbb』が続く場合に一致。『bbb』は検索結果に含めない。 |
aaa(?!bbb) | 『aaa』に『bbb』が続かない場合に一致。『bbb』は検索結果に含めない。 |
グループ化の例
以下、グループ化の例です。
var a = "abcdef abcxyz"; // 置換前の文字列 var b = a.replace(/abc(?=xyz)/g, "---"); // 置換 document.write(b + "<br>"); //結果を表示
【結果】
abcdef ---xyz
上記のプログラムの検索文字列『abc(?=xyz)』は、『abc』に『xyz』が続く場合に一致。ただし、『xyz』は検索結果に含めないという意味になります。
以下、もう一例です。
var a = "abcdef abcxyz"; // 置換前の文字列 var c = a.replace(/abc(?!xyz)/g, "---"); // 置換 document.write(c + "<br>"); // 結果を表示
【結果】
---def abcxyz
上記のプログラムの検索文字列『abc(?!xyz)』は、『abc』に『xyz』が続かない場合に一致。ただし、『xyz』は検索結果に含めないという意味になります。
それぞれ、『?』のあとが『=』なら『一致』、『!』なら不一致と覚えておくとよいです。
『正規表現』の『フラグ』
JavaScriptの『正規表現』では、『フラグ』として以下の文字を利用できます。
フラグ | 説明 |
---|---|
g | 全部を置換する (gがなければ最初の1つのみを置換) |
i | 英語の大文字、小文字を区別せずに扱う (iがなければ区別する) |
m | 文頭『^』や文末『$』の一致処理を、各行の行頭や行末にも適用する (mがなければ、文頭『^』や文末『$』は文字列の先頭と末尾のみに適用) |
『フラグ』は『ig』のように、複数指定しても構いません。1つでも構いませんし、何も指定しなくても構いません。
以下、実際に『フラグ』を指定した例を示します。
var a = "abcdef ABCDEF abcdef ABCDEF"; // 置換前の文字列 var b = a.replace(/abc/gi, "---"); // 置換 document.write(b + "<br>"); // 結果を表示 var c = a.replace(/abc/g, "---"); // 置換 document.write(c + "<br>"); // 結果を表示 var d = a.replace(/abc/, "---"); // 置換 document.write(d + "<br>"); // 結果を表示
【結果】
---def ---DEF ---def ---DEF ---def ABCDEF ---def ABCDEF ---def ABCDEF abcdef ABCDEF
最初の例では、『g』『i』と2つの『フラグ』を指定しています。そのため、一致した全ての文字列を対象にして、大文字小文字の区別を付けずに、置換を行っています。
2番目の例では、『g』1つだけ『フラグ』を指定しています。そのため、一致した全ての文字列を対象にして、大文字小文字の区別を付けて、置換を行っています。
3番目の例では、『フラグ』を指定していません。そのため、一致した最初の文字列を対象にして、大文字小文字の区別を付けて、置換を行っています。
- 正規表現 - JavaScript | MDN
- とほほのJavaScriptリファレンス - 正規表現(RegExp)
- 「正規表現と任意の1文字」~マンガでプログラミング用語解
- 「正規表現と繰り返し」~マンガでプログラミング用語解
- 「正規表現と最長最短一致」~マンガでプログラミング用語解
- 「正規表現と文字セット」~マンガでプログラミング用語解
- 「正規表現とg, iフラグ」~マンガでプログラミング用語解
- 「正規表現と先頭と末尾」~マンガでプログラミング用語解
- 「正規表現とORとグループ」~マンガでプログラミング用語解
- 「正規表現と後方参照」~マンガでプログラミング用語解
『無名関数』を使った『置換』
『replace』メソッドの『置換文字列』部分には、文字列だけでなく、関数を指定することができます。これは、配列のソート処理の引数に、関数を指定したことに似ています。
【正規表現の書き方2】 文字列オブジェクト.replace(/【検索する文字列】/【フラグ】,【 置換処理を行う関数オブジェクト】)
上記の書き方で、『replace』メソッドの引数に、関数オブジェクトを指定した実例を示します。
var a = "掛け算12 掛け算34 掛け算56"; // 置換前の文字列 var b = a.replace(/掛け算(\d)(\d)/g, function (str, s1, s2) { var calc = s1 * s2; return "結果" + calc; }); // 置換 document.write(b); // 結果を表示
【結果】
結果2 結果12 結果30
上記のプログラムの説明を行います。
『replace』メソッドの引数に指定した関数オブジェクトの、最初の引数『str』は、検索に一致した全体を表します。
2つ目の引数『s1』は、最初に『(~)』に一致した部分になります。また、3つ目の引数『s2』は、2番目に『(~)』に一致した部分になります。以降、引数を増やせば、『(~)』に対応した引数を1つずつ増やせます。これらの2番目以降の引数は、置換文字列で指定した『』『』などと同じ内容になります。
上記のプログラムでは、『(\d)』『(\d)』とすることで、『s1』に1文字目の数字を、『s2』に2文字目の数字を格納しています。最初の一致部分『掛け算12』では、『s1』が『1』、『s2』が『2』になっています。そして、関数内で計算を行い、戻り値として『結果2』という文字列を作成しています。
置換処理を行う関数オブジェクトでは、戻り値の内容が、置換される文字列になります。そのため、『掛け算12』は、戻り値である『結果2』に置換されます。
続けて、『掛け算34』では、戻り値が『結果12』、『掛け算56』では、戻り値が『結果30』になります。最終的に、『掛け算12 掛け算34 掛け算56』という元の文字列は、『結果2 結果12 結果30』という文字列に置換されます。
『正規表現』オブジェクト
『正規表現』は、『/~/フラグ』と書いてきました。しかし、これは正規表現オブジェクトを簡略化した、特殊な書き方です。
実際は『RegExp』という正規表現オブジェクトが内部的には使われており、このオブジェクトが、置換や検索の引数として利用されます。
この『RegExp』は、『new』を使い、オブジェクトとして作成して、変数に格納できます。何度も使う検索式は、変数に入れて再利用するとよいです。
var re = new RegExp("abcd", "ig"); // 正規表現オブジェクトを作成 var a = "abcd aBCd aXYd"; // 置換前の文字列 var b = a.replace(re, "----"); // 置換 document.write(b); // 結果を表示
【結果】
---- ---- aXYd
この正規表現オブジェクトは、『/~/フラグ』という書き方で作成することもできます。
var re = /abcd/g; // 正規表現オブジェクトを作成 var a = "abcd aBCd aXYd"; // 置換前の文字列 var b = a.replace(re, "----"); // 置換 document.write(b); // 結果を表示
【結果】
---- aBCd aXYd
『正規表現』による検索
『正規表現』を利用すれば、複雑な検索を行えます。『正規表現』の検索方法として代表的なものを、以下に示します。
書き方 | 意味 |
---|---|
文字列.match(正規表現) | 文字列の『一致した部分の情報』、もしくは『null』を返す |
文字列.search(正規表現) | 文字列の『一致した位置』、もしくは『-1』を返す |
『match』- フラグに『g』がない場合
『match』メソッドの戻り値には、検索結果にまつわる多くの情報が含まれています。その内容は、正規表現に『g』フラグがあるかどうかで変わります。
以下、まずはフラグに『g』がない場合(一致は1つ)の例を示します。
var a = "mn abcd aBCd aXYd mn"; var res = a.match(/a(.)(.)d/i); if (res !== null) { document.write("入力文字列 : " + res.input + "<br>"); document.write("一致位置 : " + res.index + "<br>"); document.write("結果格納数 : " + res.length + "<br>"); // 0は一致した文字列全体、1以降は丸括弧の中身 for (var i = 0; i < res.length; i ++) { document.write("結果" + i + " : " + res[i] + "<br>"); } }
【結果】
入力文字列 : mn abcd aBCd aXYd mn 一致位置 : 3 結果格納数 : 3 結果0 : abcd 結果1 : b 結果2 : c
検索結果を、『if (res !== null)』のように『if』文で囲っていることも注目してください。検索した結果、一致する部分がない場合には、『res』は『null』になります。そのため、検索が一致する場合のみ、情報を取り出しています。
一致した場合、『match』の戻り値は配列です。通常の配列に『input』『index』というプロパティが追加されています。
書き方 | 意味 |
---|---|
res.input | 入力文字列 |
res.index | 一致の位置 |
res.length | 結果格納数 |
res[0] res[1] res[2] : |
一致の全体 最初の丸括弧の中身 次の丸括弧の中身 : |
『match』- フラグに『g』がある場合
次はフラグに『g』がある場合(一致は複数)の例を示します。
var a = "mn abcd aBCd aXYd mn"; var res = a.match(/a(.)(.)d/ig); if (res !== null) { document.write("結果格納数 : " + res.length + "<br>"); // 一致した文字列全体が順に入っている for (var i = 0; i < res.length; i ++) { document.write("結果" + i + " : " + res[i] + "<br>"); } }
【結果】
結果格納数 : 3 結果0 : abcd 結果1 : aBCd 結果2 : aXYd
一致した場合、『match』の戻り値は配列です。
書き方 | 意味 |
---|---|
res.length | 結果格納数 |
res[0] res[1] res[2] : |
最初の一致 次の一致 その次の一致 : |
『search』
『search』の例も示します。『search』の戻り値は、一致した位置(0から始まる)になります。
var a = "mn abcd aBCd aXYd mn"; var pos = a.search(/a(.)(.)d/i); document.write("一致位置 : " + pos);
【結果】
一致位置 : 3
サンプルの入手
以下は、今回出てきたサンプルです。
ZIPでまとめてダウンロード (右クリックから保存してください)
『sample1.html』(感想文の置換)を表示
『sample2.html』(検索結果の再利用)を表示
『sample3.html』(説明中の置換処理まとめ)を表示
『sample4.html』(『無名関数』を利用した置換)を表示
『sample5.html』(正規表現オブジェクト)を表示
『sample6.html』(正規表現による文字列検索)を表示
プログラムの中身を見たい場合は、それぞれのHTMLファイルをブラウザで開いたあと、右クリックをして『ソースの表示』を選択してください。
メモ帳で、ファイルの中身を見ることができます。