みこむらめもむらむらむら

なんかHTML5とかJS勉強とかやりながらめもを綴るブログ

【JavaScript】正規表現で文字を自在に指定する(RegExpオブジェクト)②

はい次いきます

正規表現による検索の基本

RegExpオブジェクトの生成の次は
いよいよ文字列を検索する方法を見ていくことにする

以下は文字列からURL文字列を抽出するためのサンプル

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/gi;
var str = 'このブログはhttp://micomura.hatenablog.jp/です。';
str += 'ちなみにTwitterHTTPS://twitter.com/micomuraだったりします。';
var result = str.match(p);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

http://micomura.hatenablog.jp/
HTTPS://twitter.com/micomura

おおーできた!

正規表現で検索するには、String.matchメソッドを利用する
(正確にはRegExp.execメソッドでも可能だがこちらは後述)

vは正規表現パターンにマッチした文字列を配列として返す
ここではforループで得られた配列の内容を順に出力している

正規表現のオプションでマッチング時の挙動を制御する

静企業玄以は「g」「i」「m」という3つのオプションがあり
これらを利用すれば正規表現検索の挙動を制御できる
さっきの例だとオプションを"gi"と設定しているので
「文字列全体」を「大文字/小文字区別なし」で検索している

それぞれのオプションを外してみると
どのように動作が変わるかみてみる

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/i;
var str = 'このブログはhttp://micomura.hatenablog.jp/です。';
str += 'ちなみにTwitterHTTPS://twitter.com/micomuraだったりします。';
var result = str.match(p);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

http://micomura.hatenablog.jp/
undefined
hatenablog.
/

"g"を外してグローバル検索を無効にしているため
最初に一致した文字列が見つかったところで検索を終了している
この場合、searchメソッドは
「最初に一致した文字列全体とサブマッチ文字列」を配列として返す
サブマッチ文字列とは正規表現パターンの中に
丸カッコで示された部分(サブマッチパターン)に
合致した部分文字列のこと

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/i;

ほーなるほど
この場合下記のようなサブマッチパターンに対して
それぞれ文字列が返ってきてるわけですね

  • (s)
    • undefined
    • マッチしている部分がない場合はundefined(または空文字)が返ってくる
  • ([\w-]+\.)
    • hatenablog.
    • 複数回マッチしている場合には最後にマッチしたもののみを保持
  • (\/[\w- .\/?%&=]*)
    • /

わかりました

それでは今度は"i"を外してみますよ

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/g;
var str = 'このブログはhttp://micomura.hatenablog.jp/です。';
str += 'ちなみにTwitterHTTPS://twitter.com/micomuraだったりします。';
var result = str.match(p);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

http://micomura.hatenablog.jp/

大文字/小文字の違いを無視しない(区別する)ように
設定しているため「http(s)://」は「HTTPS://」にはマッチしない
結果「http://micomura.hatenablog.jp/」だけが返ってくる
うむ、そりゃそうだ、わかりやすい

ややわかりにくいのがmパラメータ(マルチラインモード)
別のサンプルで一つ

var p = /^[0-9]{1,}/g;  //文字列先頭にある1文字以上の数値を検索
var str = '101匹ワンちゃん\n7人の小人';
var result = str.match(p);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

101

マルチラインモードを無効にしている場合
正規表現パターン「^」は単に文字列の先頭を表すため
先頭の「101」にのみ合致する
これをマルチラインモードを有効にすると

var p = /^[0-9]{1,}/gm;  //行頭にある1文字以上の数値を検索
var str = '101匹ワンちゃん\n7人の小人';
var result = str.match(p);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

101
7

マルチラインモードを有効にすると「^」は「行頭」を表す
結果改行コード「\n」直後にある「7」にもマッチする
なるほどなーふむふむ
また、これは「$」(文字列末尾)についても同様で
マルチラインモードを有効にした場合
「$」は「行末」を表す、はあく

また「/パターン /オプション」はそれ自体がリテラル表現のため

var result = str.match(/^[0-9]{1,}/gm);

このような記述もすることができる、ふむ

matchメソッドとexecメソッドの挙動の違い

正規表現の文字列検索をするには

  • String.matchメソッド
  • RegExp.execメソッド

を利用するが
これらのメソッドによって得られる結果は
条件によって異なる場合があるため注意が必要

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/gi;
var str = 'このブログはhttp://micomura.hatenablog.jp/です。';
str += 'ちなみにTwitterHTTPS://twitter.com/micomuraだったりします。';
var result = p.exec(str);
for(var i = 0; i < result.length; i++){
  document.writeln(result[i]);
}

http://micomura.hatenablog.jp/
undefined
hatenablog.
/

execメソッドはグローバル検索(gオプション)が
有効か否かに関わらず
一度の実行で一つの結果しか返さない
代わりにマッチ文字列全体とサブマッチ文字列を
配列として返す

execメソッドでグローバル検索をした際と同じ結果を得るためには
RegExpオブジェクトの

  • 最後にマッチした文字位置を記憶する機能
  • 次回のexecメソッドを実行する際、前回のマッチ位置から検索を再開する

という特性を利用して

var p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/gi;
var str = 'このブログはhttp://micomura.hatenablog.jp/です。';
str += 'ちなみにTwitterHTTPS://twitter.com/micomuraだったりします。';
while((result = p.exec(str)) != null){
  document.writeln(result[0]);
}

このように記述することで望んだ結果が得られるようになる
execメソッドは次の検索結果がない場合にnullを返す
検索を繰り返し
最終的に検索結果が返ってこなくなると
nullが返ってくるためwhile命令を脱出する
なるほどなーなるほどなー