「プログラミング経験はそこそこだけど、JavaScriptはあんまり」な人向け社内勉強会資料vol.3
Shogo Ohta, 2009-06-30正規表現(せいきひょうげん、regular expression)とは、文字列の集合を一つの文字列で表現する方法の一つである。正則表現(せいそくひょうげん)とも呼ばれ、形式言語理論の分野では比較的こちらの訳語の方が使われる。正規表現 - WikiPedia
文字列の集合を一つの文字列で表現する→一定のルールに従って(短い)文字列で(長い)文字列を表現する
"regular expression".match(/[a-z]+/g) // ["regular", "expression"]
\^$*+?.(x)x にマッチし、マッチしたものを記憶します(?:x)x にマッチしますが、マッチしたものを記憶しませんx(?=y)x に続いて y が現れる場合にのみ、x にマッチします【肯定的前方先読み】x(?!y)x に続いて y が現れない場合にのみ、x にマッチします【訳注: 否定的前方先読み】x|yx または y にマッチ{n}n 回現れているものにマッチ{n,}n 回現れているものにマッチ{n,m}n 回、多くとも m 回現れているものにマッチ[xyz][^xyz]\d\D\n\r\s\S\t\w\W\n\0\xhhhh(2 桁の16 進表現)で表される文字にマッチします\uhhhhhhhh( 4 桁の 16 進表現)で表される Unicode 値の文字にマッチします正規表現リテラルは正規表現をJavaScript中に書く際の簡易記法です
new RegExp('\\d')
は
/\d/と同等
文字列リテラルで正規表現を書く場合、\は\\として2重にエスケープする必要がある。これは文字列リテラルが\をエスケープシーケンスとして使用し、さらに正規表現でも\をエスケープシーケンスとして使用するので、2回エスケープが必要になるためである。
逆に、正規表現リテラルでは/でクオートしているので、/自体を使用したい場合は/をエスケープする必要がある。
つまり、文字列リテラルか、正規表現リテラルかでエスケープすべき文字が異なるので注意が必要です。
RegExpオブジェクトはtest、execというメソッドを持つ。
var exp = new RegExp('\\d');
console.log(exp.test('1')); // true
console.log(exp.exec('1')); // ["1"]
Stringオブジェクトは正規表現関連のメソッドとしてmatch、replace、split, searchを持つ。(searchはマッチした位置を数値で返します。使い道は特にないのであまり使用しません…)
var str = '1';
console.log(str.match(/\d/)); // ["1"]
console.log(str.replace(/\d/,'a')); // a
console.log('1,,2,3:4-5'.split(/\D/));
// ["1", "", "2", "3", "4", "5"]
// ただし、IEは
// ["1", "2", "3", "4", "5"]
// になる。空の要素は省略されるので注意が必要です。
RegExp.testは単純にマッチするか否かという真偽を判定して真偽値を返すので、正規表現関連のメソッドでもっとも高速に動作します。splitでの正規表現はIEの挙動の違いなどもあって、あまり使用しないほうが良いです(正規表現ではなく、文字列でsplitするなら特に問題はない)。
マッチした部分を抜き出して利用する場合、String.matchが扱いやすいのでよく利用される。ただし、matchは引数に文字列を渡した場合、暗黙的に正規表現に変換される点には注意が必要です。
var str = "index?id=123";
var str2 = "index?ids=123";
var id = str.match("id=(\\d+)")[1];//(1)
console.dir(RegExp);
var m,id2 = '';
if ((m = str2.match("id=(\\d+)"))) {
id2 = m[1];
}
var id3 = '', exp = /id=(\d+)/;
if (exp.test(str)) {
id3 = exp.exec(str)[1];
}
console.log([id,id2,id3]);
正規表現を実行するとその結果はRegExpの$1~$9に代入される。この値を使うコードを見かけることも多いが、これはオススメできない。後々になってmatchとRegExp.$1を参照する間に処理を追加して、その間に正規表現を使用しする処理が入ると予期せぬ結果になる。
var str = "index?id=123";
if (str.match('id=(\d+)')){
some_function();// ここで正規表現が使われると結果が変わる危険がある
id = RegExp.$1
}
matchの結果は配列になるので、(1)のように書くことで1つ目の()の中身を取り出すことが出来る。ただし、この方法ではmatchしなかった場合にnullに対してプロパティ参照をしてしまうのでエラーになる可能性がある。
そこで、マッチの結果を変数に代入し、代入できていたらそこから値を取り出すという処理か、マッチングするかtestを行い、成功したら値を取り直すという処理が望ましい。
ちなみに下記のようにして強引に一行で書くことも可能
var str2 = "index?ids=123";
var id = (str2.match("id=(\\d+)")||'')[1];
var m,id2 = ((m = str2.match("id=(\\d+)"))) ? m[1] : '';
正規表現リテラルでgを付けるか、RegExpの第二引数でgを渡すと一度の正規表現ですべてのマッチングを取得できる。
var strs = "id=123&id=456&id=789";
var ids = strs.match(/id=\d+/g)||[];
console.log(ids);
String.replaceは文字列を置換するメソッドで頻繁に使用される。特に第二引数に関数を渡すことで柔軟な処理ができる。
var data = {
name:'simple template',
discription:'シンプル且つクロスブラウザなテンプレートです'
};
var tmpl = "簡易テンプレートの実装例:#{name}\n説明:#{discription}";
console.log(tmpl.replace(/#\{([^}]+)\}/g,function(_,$_){
return data[$_] || '';
}));
String.prototype.fill=function(data){
return this.replace(/#\{([^}]+)\}/g, fill);
function fill(_,$_){
return data[$_] || '';
}
};
console.log(tmpl.fill(data));
URLにマッチする正規表現を考える
var strs = [
'http://albert2005.co.jp/',
'http://www.albert2005.co.jp/'
];
strs.forEach(function(str){
console.log(/^https?:\/\/(www\.)?albert2005\.co\.jp/.exec(str));
});
さらに、パラメータから特定の値を取り出す
var strs = [
'/foo/item?NO=4837',
'/foo/item?hoge=1&NO=4837',
'/foo/item?NO=20309&hoge=2'
];
strs.forEach(function(str){
console.log(/^\/foo\/item\?NO=(\d+)/.exec(str));
console.log(/^\/foo\/item\?(?:.*)NO=(\d+)/.exec(str));
});
さらに、肯定的前方先読み、否定的前方先読みで特定の値を取り出す
var strs = [
'/1234/id/',
'/12343/',
'/1234/notid/'
];
strs.forEach(function(str){
console.log(/^\/(\d+)(?=\/id)/.exec(str));
console.log(/^\/(\d+)\/(?!notid)/.exec(str));
});
サブドメインに関わらずマッチさせるには?
var strs = [
'http://www.albert2005.co.jp',
'http://albert2005.co.jp',
'http://sub.albert2005.co.jp'
];
strs.forEach(function(str){
console.log(/^http/.exec(str));
});
var strs = [
'http://www.albert2005.co.jp',
'https://www.albert2005.co.jp',
'http://albert2005.co.jp',
'http://sub.albert2005.co.jp',
'http://sub2.sub.albert2005.co.jp'
];
strs.forEach(function(str){
console.log(/^https?:\/\/(?:[^.]+\.)*albert2005\.co\.jp$/.exec(str));
});