スタートページJavascript

正規表現(regular expression)の基礎

正規表現に関しては、多くの解説書やWebサイトがあります。ここで取り上げたものは一部分にすぎず、しかも初歩的なものです。あえてこれを作成したのは、script作成時にうまく処理できなかったときのテスト用にしたかったからです。

実例は「正規表現のテスト」にあります。HTML内部に記述した内容を同じHTMLの異なる場所にコピーする例は「正規表現によるHTML内部のコピー」にあります。


正規表現による検索・置換の一般形式

一般的には、入力文にある文字列があるか(一致するか)を検索し、一致したらどうするかを記述するのですから、その一般形式は
   出力文 = 入力文.プロパティ(検索文字列 [, パラメタ])
の形式になります。

主なプロパティには、次のものがあります。
  match ・・・検索(一致した文字列を戻す)
  search・・・検索(一致した最初の文字列の位置を戻す)
  replace ・・置換

例えば、検索文字列を「ab」として、一致した部分を「xyz」に置換する場合はreplaceというメソッドを用います。この検索文字列を正規表現することにより、
  最初に一致した部分だけを置換
    出力文 = 入力文.replace(/ab/, "xyz");
  すべて一致した部分を置換
    出力文 = 入力文.replace(/ab/g, "xyz");
  大文字・小文字の区別をせず、「ab」「Ab」「aB」「AB」を置換
    出力文 = 入力文.replace(/ab/i, "xyz");
  「aib」や「aijkb」のように、aとbで囲まれた部分をすべて置換
    出力文 = 入力文.replace(/a.?b/g, "xyz");   など、複雑な検索ができるのです。→後述「正規表現の記法」

「i」や「g」を検索修飾子といいます。
  g・・・「すべての」→指定しないときは最初に一致したものだけ
  i・・・「大文字・小文字を区別せずに」→指定しないときは区別する
  m・・・「複数行ある場合はすべての行を対象に」→1行のときは指定しない

正規表現を変数で与えるとき、
    検索文字列 = "/ab/i";
    出力文 = 入力文.replace(検索文字列, "xyz");
とすることができません。正規表現は特殊なのです。
    検索文字列 = "ab";
    検索修飾子 = "i";
    正規表現 = new RegExp(検索文字列, 検索修飾子);
    出力文 = 入力文.replace(正規表現, "xyz");
とします。

matchの結果

「結果 = 入力文.match(正規表現);」として、「結果」を表示するとマッチした文字列が、「,」で区切られて、「文字列0, 文字列1, ・・・,文字列n-1」のように表示されます。
 ところが実際には、結果[0]=文字列0, 結果[1]=文字列1,・・・, 結果[n-1]=文字列n-1 の配列になっているのです(g指定をしないときは、結果[0] だけになります)。
 その配列の個数nは「結果.length」で得られます。

正規表現の記法

正規表現例示一致する条件
.A.Aの次に改行コード以外の任意1文字
*A*0個以上連続したA
.*A.*BAからBまでの間の任意文字列、最長一致(注1)
?AB?CAとCの間に直前の文字Bがないか,Bがある(AC, ABC)
.*?A.*?BAからBまでの間の任意文字列、最短一致(注1)
+A+1個以上連続したA
^^ABC「^」は行頭を示す。行頭が「ABC」(注2、注4)
$ABC$「$」は行末を示す。行末が「ABC」(注2)
{n}A{3}3個のA(AAA)
{n,}A{3,}3個以上のA
{n,m}A{2,4}2個以上,4個以内のA
[  ][ABC]AまたはBまたはCの何れか1文字
|AB|XYZ「AB」または「XYZ」と一致する文字列(注3)
[*-*][a-z]a~zの何れか,アルファベット小文字1文字(注4)
[*-*]+[A-Z}+アルファベット大文字列
(  )+(AB)+( )はグループ化。「AB」の繰返し、AB, ABABABなど
\n\n改行コード
\r\r復帰コード
\d\d数値文字(0~9)
\D\D数値文字以外
\w\wa~z,A~Z,0~9あるいは「_」
\W\W\w以外の文字
\s\s半角空白文字。改行コードを含み全角空白は含まない(注5)
\S\S\s以外の文字
\b\b単語の区切り(半角空白で囲まれた文字列の先頭)、行頭
\B\B\b以外の文字
(?=…)\d+(?=円)「円」の直前にある数字列→「先読み機能」参照

上表のように、正規表現では「. * + ? ^ $ | [ ] { } ( )」の文字(メタ文字という)は特殊な意味をもっています。それで、「.」や「*」を文字自体として用いるときは、直前に\(エスケープ文字という)をつけて、「\.」や「\*」のように記述します。また「\」自体を文字とするときには「\\」と記述します。

いくつかの例

グループ化検索とRegExp.$n

正規表現「( )」でのグループによる検索では、一致した文字列をRegExp.$n(n=1~9)に保存して利用する機能があります。
 入力文 = "abc123"; 正規表現 = "/([a-z]+)([0-9]+)/"; 結果 = 入力文,match(正規表現);
としたとき、1番目の([a-z]+)で一致するのは「abc」、2番目の([0-9]+)で一致するのは「123」です。
 このとき、RegExp.$1には"abc"、RegExp.$2には"123"が保存されます。
 $nの個数には制限があり、$1~$9までです。

そして、他の処理でRegExp.$nを使うことができます。
 例えば、入力文 = "xyz789"; 出力文 = 入力文.replace(/xyz/, RegExp.$1);
とすると、出力文 = "abc789" になります。

先読み機能

「1人あたり2台のパソコンと300円の現金と1000円の預金がある。」(数字は半角)の入力文があり、これから「300」「1000」という文字列を得たいとします。
 正規表現を数字列「\d+」としたのでは「1」人や「2」台まで拾ってしまいます。
 「\d+円」とすると「300円」「1000円」のように「円」がついてしまいます。
 「(?=円)」とすると、まず「円」を探し(先読みして)、検索位置は「円」の直前を指しています。それで、「\d+(?=円)」により円の直前の数字列が得られるのです。
 「\d+(?=円|千円)」のような記述もできます。

「(?=¥)\d+」(通貨記号を全角の¥にしています)としたら「¥」に続く数字列が得られるのではありません。(?=¥)を先読みしたときに、検索位置は「¥」の直前を指しているので、「¥」までが含まれてしまいます。それでは「¥\d+」としたのと同じです。
 「~(?!=…)」の形式は便利ですが、「(?!=…)~」は使う機会はないように思います。

なお、「先読み」ではなく「戻り読み」という機能もあるのですが、Javascriptでは実装されていないようです。