お楽しみプログラム

全般

JavaScriptでの正規表現

JavaScriptでの正規表現の話題です。


JavaScriptで正規表現を使うには

良くある、正規表現のための オブジェクト RegExp についてと、String(文字列の)正規表現関係の関数についてまとめてみました。

とりあえず、マイクロソフト版です。

まずはイントロ

正規検索は、RegExp オブジェクトのメソッドから、あるいは、String オブジェクトのメソッドから 利用できる。まず、最初に RegExp オブジェクトから見ていこう。

RegExp は JavaScript において特殊な位置にあり、「リテラル記述」ができる。

例えば次のように書く。

var e=/abc.d/i;

/abc.d/が検索パターンで、i がオプション。このオブジェクトが e にセットされている。

通常のオブジェクトのように new を使って生成できる。

var e= new RegExp("abc.d","i");
RegExp のコンストラクタ

上記のように二通りの書き方があるが、いずれも、パターンとフラグで構成されている。

new RegExp( pattern [, flags ])

pattern , flags ともに文字列。flags は、flag に相当する文字を並べたもの

例: var pat=new RegExp("mm\d+","i");
/ pattern /[flags]

flags は flag に相当する文字を連ねたもの

例: var pat=/mm\d+/i;

たぶん仕様外」だろうが、次の書き方もできた。

new RegExp( RegExp )

引数にRegExpオブジェクトを指定すると、同じものを作ってくれるようだ。

例1: var pat=new RegExp(/mm\d+/i);
例2: var re1=/abc/i; var re2=RegExp(re1);

 許されるフラグは次の通り

i 大文字小文字を区別しない。

内部ではユニコードで処理しているので、半角英数字だけでなく、それなりの大文字小文字の区分を無効にするようです。例えば、全角のAとaは同じ文字となります。

全角文字について少し調べました。

ひらがなぁ : あマッチしない
カタカナァ : アマッチしない
全角英字a : Aマッチする
全角ギリシャω : Ωマッチする
全角ロシアа : Аマッチする
全角ローマ数字E : Yマッチする
g グローバル検索。

パターンに合致する文字列を先頭から順にすべて検索するためのパターン。詳細は各メソッドの記述を確認してください。

m 複数行検索。

通常は、パターンの中では、^ は検索対象文字列の先頭位置を示し、$ は文字列の末尾位置を示します。このフラグを指定すると、この機能に加えて改行コードとの一致ができるようになります。

^ は、\n または \r の直後の位置と一致。

$ は、\n または \r の直前の位置に一致。

ちょっと、インタールード

RegExp は、オブジェクトのプロパティ/メソッドだけでなく、クラスのプロパティを持っています。クラスのプロパティは、オブジェクトごとに存在するオブジェクトのプロパティと異なり、プログラム中に一組しか存在しません。例えば、プロパティ index ならば、次のようにクラス名で修飾して参照します。

 RegExp.index

RegExp のメソッドや、String のメソッドで正規表現検索ををすると、部分文字列など、結果にまつわるこまごまとした情報がセットされるので、検索処理終了後に参照することができるので便利ですが、一組しか保持できないという使いにくさもあります。

RegExp のクラス・プロパティ

検索結果を保持する、プログラム中に一組だけの情報です。

プロパティ略記名初期値
index-1 一致文字列の先頭位置
input$_ "" 対象文字列
lastIndex-1 一致文字列の末尾位置(次の文字)
lastMatch$&"" 正規表現による検索で最後に一致した文字列。
lastParen$+"" 最後に見つかった部分文字列の値
leftContext$`"" 一致文字列の前までの文字列。
rightContext$'"" 一致した文字列の後にある文字列。
$1 - $9"" 部分文字列の値

略記法による参照は、$1-$9 を除いて、文法的に許されない名称なので、文字列を使ったプロパティ参照を使用する。すなわち、

RegExp["$&"]

のように書く。

RegExp のオブジェクト・プロパティ

パターンに設定したのフラグをここで読み取ることができる

以下のプロパティはいずれも、読み取り専用となっている。

globalboolean"g" フラグを反映
ignoreCaseboolean"i" フラグを反映
multilineboolean"m" フラグを反映
sourcestringパターンソース
lastIndexint(非公開?)検索位置。書き込める
RegExp のオブジェクト・メソッド
compile( pattern_string [, flags ])
compile(/ pattern /[ flags ])
RegExp

内部形式に変換する。pattern,flagsは new と同じ。

戻り値は、RegExpオブジェクト自身(らしい)。

例1: var re=/abc/g; ... re.compile("abc","g");
例2: var re=/abc/g; ... re.compile(/abc/i);

仕様外だろうが、引数にRegExpを指定できる。(あまり意味がないが…)

例3: var re=/abc/i; ... re.compile(/abc/g);
例4: var re1=/abc/i; var re2=/xyz/; re2.compile(re1);
exec(str)Array of string

文字列 str に対して、一回だけ検索。結果を文字列の配列で返す。

見つからなかった場合には、nullを返す。(RegExpのクラスプロパティは変わらない)

見つかった場合は、配列を返し、RegExpのクラスプロパティを書き換える。

戻り値の配列の内容は次の通り。

配列[0]
一致した文字列
配列[n]
部分文字列n
以下、部分文字列が続く
input
検索対象の文字列全体
index
一致の先頭
lastIndex
一致の末尾(次の文字位置)

パターンの g フラグで動作が異なる。

g がなかったとき
常に先頭から検索する。
g があったとき
パターンに保存された次の検索位置(オブジェクトのlastIndex)から検索する


g フラグをセットして、グローバル検索をするときは、execが一度しか検索を行わないので、手動で繰り返す必要があるが、string.match()と異なり、毎回の検索ごとの部分文字列など、最も多くの情報を得ることができる。

また、検索の結果、一致文字列の末尾位置がオブジェクトの内部(lastIndex)にセットされ、次の検索ではその位置から行うので文字列を順次検索できる。

次のように書く。

var re=/\d+/g;
var str="abc123def456";
var result;
while( result=re.exec(str) )
  { // result[0];
  }

次のように書いても、RegExpオブジェクトが異なると、検索が継続されない。

var re1=/\d+/g;
var re2=/[a-z]+/g;
var str="abc123def456";
var result;
if( result=re1.exec(str) )
  { // result[0] : "123"
  }
if( result=re2.exec(str) )
  { // result[0] : "abc"
  }

RegExpオブジェクトが同じならば、文字列が変わっても、検索位置は保持される。

var re=/\d+/g;
var str1="abc123abc456xyz";
var str2="xyz789zzz997"
 if(result=re.exec(str1) )
  { // result[0] : "123"
  }
 if(result=re.exec(str2) )
  { // result[0] : "997"
  }

上記の例は、g フラグをセットしたパターンを、文字列を次々に適用する場合には、問題が生じることを示している。 文字列ごとに内部の検索ポインターを初期化する(lastIndexを0にする)必要がある。。

オブジェクトの lastIndex(文字列先頭は0) プロパティを操作することで、検索開始位置を制御できる。

var re=/\d+/g;
var str="abc123abc456xyz";
re.lastIndex=6;
 if(result=re.exec(str) )
  { // result[0]) :  "456"
  }
re.lastIndex=0;
 if(result=re.exec(str) )
  { // result[0]) :   "123"
  }

test(str)boolean

文字列 str に対して、一度だけ検索。成否を booleanで結果として返す。

成立した場合 、RegExpのクラスプロパティも書き換えられる。

内容が返されないことを除けば、exec と同じように動作すると考えられる。

g フラグがない場合
文字列の先頭から検索する。
g フラグがある場合、パターンに保存された次の検索位置から検索を行う。

普通ならば、exec を使えば十分。test は配列を返さないので、メモリの消費が多少少ない点がメリット。

toString()string

検索パターンに相当する文字列を返す。

例: re=new RegExp("abc","g"); str=re.toString(); "/abc/g" が返る
String で使える 正規表現メソッド

文字列のオブジェクトには正規表現の検索、置換などのメソッドが用意されています。

match(regExp)Array

検索結果を配列で返す。パターンに g フラグが指定されている場合は一括検索をする。

パターンに一致する文字列が見つからなかった場合は、null を返す。(RegExpのクラス・プロパティは変わらない。)

一致する文字列が見つかった場合は、配列を返す。RegExpのクラス・プロパティに値をセットする。

戻り値の配列の内容は、g フラグの有無で異なる。

g フラグが設定されていない場合、
配列[0] 一致文字列
配列[n] 部分文字列n
g フラグが設定されている場合、
配列のメンバーは、見つかった一致のすべての並び。

また、この配列は以下のプロパティを持つ。

input
検索対象文字列。
index
一致文字列の先頭位置。g フラグがセットされている場合は、最後の一致文字列の先頭位置。
lastIndex
一致文字列の末尾位置。g フラグがセットされている場合は、最後の一致文字列の末尾位置。

引数が文字列の場合も仕様外だろうが、動作する。 与えた文字列はパターンを表すが、「\」エスケープを考慮して記述する必要がある。

	"abc".match(/\w+/)
	

ならば

	"abc".match("\\w+")
	

と書く。ただし。フラグをセットできないので使用することはないと思う。

replace(srcText,repText)String

文字列中のsrcTextをrepTextに置換。置換後の文字列のコピーを返す。

srcTextは、文字列または、RegExp オブジェクトを指定できる。

この関数の動作は、srcText が文字列の場合、RegExp では g フラグの有無で動作が大きく異なる。

srcText が文字列の場合

repText には通常文字列を置く。このとき、srcTextをrepTextに置き換えるが、この置き換えは一度だけ行われる。

repText には関数を指定できる。この関数は三つの引数を取り、置換文字列を返却する。

function myfunc(match_str,index,text)
{
 // .....
 return "XYZ";		// 置換文字列
}

引数の意味は次の通り

match_str一致文字列
index一致文字列の位置
test検索対象文字列の全体

srcText が RegExp の場合

パターンに g フラグがセットされていない場合には、置換は一度だけ行われる。g フラグがセットされている場合は、連続して置換が行われる。

置換が行われた場合は、RegExpのクラス・プロパティが更新される。g フラグがセットされている場合は、最後のマッチの状態がセットされる。置換が行われなかった場合には更新をしない。

repTextが文字列である場合、置換が行われるたびに、repTextの内容に対して以下の置き換えをしてから置換をする。

$$ $
$&一致文字列。
$`一致の直前までの文字列
$'一致の後の文字列
$n指定の番号の部分文字列。n は 1 桁の 10 進数値 (1 〜 9)
$nn指定の番号の部分文字列。nn は 2 桁の 10 進数値 (01 〜 99)

repTextには関数を指定できる。この関数は置き換えが起こるたびに呼び出され、置換文字列を返す。

引数は全体で部分文字列の個数+3個ある。番号との対応を次に示す。

また、RegExpオブジェクトはこの関数が動作している間は更新されず、(置換が起これば)replaceの終了時に更新される。

0一致文字列
1 - m部分文字列
m+1一致文字列の先頭位置
m+2入力文字列の全体
search(regExp)Number

文字列を検索し、先頭からの位置を返却する。見つからなかった場合には、-1を返す。

パターンに g フラグを指定しても検索は一度しか行われない。

マッチした場合には、RegExp のクラスプロパティがセットされる。マッチしなかった場合は更新しない。


引数に文字列を指定すると、パターンとして扱われる。ただし、"\"エスケープに注意しなければならない。

split([separator [,limit]])Array

separatorを区切りとして文字列を分解し、配列を返す。limitは配列の要素数の制限(負の値は無視)。

separator は、文字列、または、RegExpを指定する。また、省略することもできる。

引数を省略した場合

入力文字列そのものをただ一つの要素にする配列を返す。

separator が文字列

separatorが null、あるいは、"" の場合は特殊な動作をする。また、limit で配列の最大の大きさを指定できる。配列の大きさを超える要素は格納されない。limitは負の値では無視され、0 では、要素のない配列になる。

separator が null の場合
入力文字列そのものを唯一つの要素とする配列を返す。
separetor が "" の場合
入力文字列を文字単位に分解して要素とする配列を返す。
それ以外の場合
separatorを区切りとして分解して要素とする配列を返す。

separator が RegExp オブジェクト

separatorを検索し、それを区切りとして要素に分解し、配列に格納して配列を返す。

separatorが見つからなかったとき、入力文字列をただ一つの要素とする配列を返す。

パターンの g フラグの有無は意味がない。

RegExpのクラス・プロパティは、最後にseparatorが見つかった状態で更新する。separatorが見つからなかった場合には更新されない。

ここでも、limitで返す配列の最大値を指定できる。負の値は無視される。0を指定すると要素のない配列を返す。


簡単なテスト

ブログでもちょっと書いた簡単な機能テストをしました(ブラウザの違いを見たかったのです)。

処理あなたのブラウザでの結果 perl (5.12) での結果
"123456789".match(/()/g) ,,,,,,,,,
"123456789".match(/(|\d)/g)) ,1,,2,,3,,4,,5,,6,,7,,8,,9,
"123456789".match(/(\d|)/g)) 1,2,3,4,5,6,7,8,9,

簡単なテスト2

処理あなたのブラウザでの結果IE8での結果
"abc123def456hij".match("\\d+") 123
"abc123def456hij".replace(/\w(\d+)/g,"$1") ab123de456hij
"abc123def456hij".replace(/\d+/g,dum)
function dum(s) { var a=parseInt(s); return a+100; }
abc223def556hij
"123456789".split("") 1,2,3,4,5,6,7,8,9
メニューへ戻る
アルゴ算法堂へ戻る