Aikの技術日記

技術的な進捗とか成果とかを細々と投稿するブログです。時々雑記も。

正規表現記法についてまとめてみた その4

その1(正規表現の概要)はこちら
その2(正規表現特殊文字について)はこちら
その3(JavaScriptでの正規表現使用法 その1)はこちら

はじめに

知ってると便利、扱うともっと便利、正規表現
前回の記事では、JavaScript正規表現を扱うためのオブジェクトや、オプションフラグについてまとめました。

今回の記事では、JavaScript正規表現を扱えるメソッドについてまとめていきます。
早速見ていきましょう。

JavaScript上での正規表現の扱い方

紹介に入る前に今回の参考記事を下記に載せておきます。
ぶっちゃけ前回とおんなじです。
正規表現 - JavaScript | MDN
正規表現 (RegExp) | JavaScript 日本語リファレンス | js STUDIO

※なお、今回は説明句だけでなくソースコードに関しても、こちらから引用させていただいたものが多々あります。

それでは行きましょう!

正規表現を扱えるメソッド

JavaScriptには正規表現に対応したメソッドがいくつかあります。
その数はMDNさんによると数個…具体的にいうと下記の6つほどです。

  • RegExpオブジェクト内のメソッド
    • exec: 文字列中で一致するものを検索するメソッド。結果情報の配列を返します。
    • test: 文字列中で一致するものがあるかをテストするメソッド。true または false を返します。
    • toString: RegExpオブジェクト用のtoStringメソッド。正規表現を表す文字列を返す。
  • Stringオブジェクト内のメソッド
    • match: 文字列中で一致するものを検索するメソッド。結果情報の配列を返します。マッチしない場合は null を返します。
    • search: 文字列中で一致するものがあるかをテストするメソッド。マッチした場所のインデックスを返します。検索に失敗した場合は -1 を返します。
    • replace: 文字列中で一致するものを検索し、マッチした部分文字列を別の部分文字列に置換するオブジェクト。
    • split: 正規表現または固定文字列を用いて文字列を分割し、部分文字列の配列に入れるメソッド。

1つ1つ使い方とともに見ていきましょう。

execメソッド

execメソッドは、対象とする文字列から正規表現パターンと一致するものを検索するメソッドです。

マッチに成功すれば、execメソッドは配列を返し、正規表現オブジェクトのプロパティ…先にあった「lastIndex」とかですね、を更新します。
マッチするものが無ければ、execメソッドはnullを返します。

なお、このexecメソッドはgフラグをつけても、最初に見つけた一致する文字列が発見されたなら、そこで実行を終了させちゃいます。
もしexecメソッドを使って複数の一致する文字列を検索したい場合は、matchメソッドを使うか、下記のようにwhileループさせちゃえば大丈夫との事。

var xArray;
while(xArray = re.exec(str))
  console.log(xArray);
// produces: 
// ["fee ", index: 0, input: "fee fi fo fum"]
// ["fi ", index: 4, input: "fee fi fo fum"]
// ["fo ", index: 7, input: "fee fi fo fum"]

testメソッド

testメソッドは、対象とする文字列が正規表現パターンとマッチするかしないかを判断するメソッドです。
マッチした場合は戻り値としてtrueを、マッチしなかった場合は戻り値としてfalseを返します。

なお、gフラグが付与された状態で複数回testメソッドが呼び出された場合、前回マッチした文字列より後の部分で、マッチするかしないかを判定します。

詳細な情報を知りたい場合はexecメソッドを使うしかないですが…。
単純に文字列があるかないかを確認したいときは、testメソッドの方が動作が軽いのでオススメとの事です。

使い方は下記に。

var regexp = /abc/;
regexp.test("123abc");  // return -> true

toStringメソッド

toStringメソッドは、RegExpオブジェクト用のtoStringメソッドです。
正確には、Object.prototype.toString()を継承しないメソッドです。

このメソッドは、正規表現を表す文字列を返します。
使い方は下記に。

myExp = new RegExp("a+b+c");
alert(myExp.toString());       // "/a+b+c/"を表示
foo = new RegExp("bar", "g");
alert(foo.toString());         // "/bar/g"を表示

matchメソッド

※ここからは、Stringオブジェクト内のメソッドに入ります。

Stringオブジェクトのmatchメソッド(以下String.matchのように記します)は、引数として正規表現を取るメソッドです。
gフラグが設定されていなければ、実行結果はRegExp.execメソッドと同じです。

gフラグが設定されている場合は、String.matchは文字列内にある、正規表現パターンに一致する全ての部分文字列を配列で返します。
どのように使うかは下記のコードを参照に…。
※なお、今回はこちらの記事を参考にコードを書きました

var regexp = /(\d{2})/g;
var str = "123456";
var results = str.match(regexp);
for (var i = 0; results.length > i; i++) {
    console.log(results[i]);
}
// 実行結果: 
// 123123
// 444456
// 789789

一見するとString.matchメソッドの方が良いようにも思えますが…。
RegExp.execメソッドを使う利点もちゃんとあるそうです。
下記の引用文を参照ください|д゚)

通常はmatch()メソッドで十分だと思いますが、exec()メソッドだからこそ、できることもありますので紹介します。
文字列'123456789'から、「3桁の連続した数字」をすべて取得します。
つまり、123, 234, 345, 456, 567, 678, 789 を取得します。
match()メソッドを使ってみると、次のようになります。

var str = '123456789';
var ex = /\d{3}/g;
var arr = str.match(ex);
console.log(arr);
// 実行結果:
// Array [ "123", "456", "789" ]

結果は、123, 456, 789 となり、すべてを取得することはできません。
取得した文字の次の文字からマッチングするのでこうした結果になります。
次はexec()メソッドを使ってみましょう。

var str = '123456789';
var ex = /\d{3}/g;
var arr = [];
while((arr = ex.exec(str)) != null){
    console.log(arr);
    ex.lastIndex -= 2;
}
// 実行結果:
// Array [ "123" ]
// Array [ "234" ]
// Array [ "345" ]
// Array [ "456" ]
// Array [ "567" ]
// Array [ "678" ]
// Array [ "789" ]

うまく取得できました。
ポイントは「ex.lastIndex -= 2;」です。
RegExpオブジェクトのlastIndexプロパティには、マッチした文字列の直後の文字位置が入っています。
この値を「-2」することによって、次のマッチングする文字位置を変更できます。
exec()メソッドはこのような使い方もできます。

※参考記事: 【JavaScript】正規表現 match() と exec() の違い

String.matchメソッドではこの様な動作は出来そうにないですしね。
住み分けがちゃんとされてるんだな…。

searchメソッド

String.searchメソッドは、正規表現パターンにマッチする文字列の開始位置を返します。
なお、マッチしなかった場合は-1を返します。

RegExp.testと挙動は似ていますが、あちらは戻り値がbooleanなのに対し、String.searchメソッドは戻り値が整数になっています。

もし正規表現では無いオブジェクトobjが渡された場合、new RegExp(obj)が暗黙的に実行されます。
このため、String.searchメソッドにかけたオブジェクトはRegExpに変換されてしまいます。

詳細な情報を知りたい場合はString.matchメソッドを使うしかないですが…。
探したい文字列がどこにあるかだけを確認したいときは、String.searchメソッドの方が動作が軽いのでオススメとの事です。
さらに、単純に文字列があるかないかを確認したいときは、RegExp.testメソッドを使えってことでしょうか…。

使い方は下記に。

var regexp = /ab/;
var str = "abcd";
console.log(str.search(regexp)); // result -> 0

replaceメソッド

replaceメソッドは、第1引数としてStringオブジェクトまたは正規表現を取り、それにマッチする文字列を第2引数の文字列で置き換えます。
gフラグがある場合、マッチする部分文字列が複数あれば、その全てに置換が適用されます。

なお、このメソッドは呼び出し元の文字列を変更しません。
ただ単純に新しい文字列を返すだけです。

簡単な例を下記に載せておきます。

var regexp = /ab/;
var str = "abcd";
console.log(str.replace(regexp, "cd")); // "cdcd"

また、キャプチャが行われた場合、replaceメソッド内に$1, $2と書くことでキャプチャ文字列自体を参照する事が可能になります。
例を下記に載せます。

// 文字の入れ替えを行う
var regexp = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(regexp, "$2, $1");
print(newstr); // "Smith John"

この$1の様な置換パターンは、下記の様なものもあるそうです。

パターン 挿入文字列
$$ "$"文字が挿入される
$& マッチした文字列部分が挿入される
$` マッチした部分文字列の前にある文字列が挿入される
$' マッチした部分文字列の後ろに続く文字列が挿入される
$n,または$nn nまたはnnの部分には数値桁が入り、このメソッドの1つ目の引数がRegExpオブジェクトで提供された際の、 n番目の丸括弧の部分マッチ文字列が挿入される

また、2つめの引数に関数を指定することもできます。
関数に渡すことが出来る引数は下記の通りです。

指定可能な名前 提供される値
match マッチした文字列部分($&に相当)
p1,p2,... キャプチャされた部分文字列($1、$2...$nに相当)
offset マッチした部分文字列の、対象としている文字列全体でのオフセット(例えば文字列全体が"abcd"であり、マッチした部分文字列が"bc"だったとすると、この引数は1になる)
string 対象となる文字列全体

下記に例を載せておきます。

function replacer(match, p1, p2, p3, offset, string){
  // p1には数値以外の文字、
  // p2には数値、
  // p3には数値とアルファベット以外の文字が格納されます。
  return [p1, p2, p3].join(' - ');
}
newString = "abc12345#$*%".replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString); // "abc - 12345 - #$*%"

splitメソッド

splitメソッドは正規表現を引数とした場合、それにマッチする部分で区切って文字列を分割します。
マッチしなかった場合は、元の文字列全体のみを含む配列が返されます。

使い方は下記に。

var str = "Hello 1 word.";
console.log(str.split(/(\d)/)); // ["Hello", "1", "word."]

※なお、引数が正規表現ではなく文字列だった場合、その文字列に完全一致する部分で区切って文字列を分割します。
普段はこの用法で用いられる…と言うより、今回の記事では「正規表現」を題にしているのでこの様な紹介となってますが…。
普段使う場合は「splitメソッドに正規表現を使おう」と言う様に、「正規表現」ばかりを意識した使い方はされないのではと思います…。

var str = "abc*def*ghi";
console.log(str.split("*")); //[abc, def, ghi]

次回記事では

次回記事では、JavaScript環境下での正規表現の実例をいくつか見ていこうと思います。
JavaScriptで、とありますが、正規表現パターン自体は他にも色々使い回せるかもしれません…。

ではでは|д゚)