JavascriptのRegExpオブジェクトをStringから生成したいです。

普通は
js> a = new RegExp("abc", "i")
js> a.source # => "abc"
とすればよいのですが、
"/abc/i"のような文字列から先のようなRegExpを作ろうと思うと
js> a = new RegExp("/abc/i")
js> a.source # => "\/abc\/i"
のようになってしまいます。そこでevalを使うと
js> a = eval("/abc/i")
js> a.source # => "abc"
のようにほしいRegExpが作れることまでわかっています。
しかし、"/abc/i"のような文字列が安全かどうかわからない場合に、evalを使いたくありません。つまり
js> a = eval("while(true){alert('abc')}")
のようなことができないようにしたいです。
RegExpで、"/abc/i"の"abc"と"i"を分離させて、そこからRegExpを新たに生成するという方法もありますが、
ちょっと泥臭いので、もう少しスマートに、"/abc/i"のような文字列から、任意のコードを実行されないように、適切なRegExpを生成することができますか?

回答の条件
  • 1人2回まで
  • 登録:2007/09/09 20:58:56
  • 終了:2007/09/10 18:04:15

ベストアンサー

id:Mug No.3

Mug回答回数15ベストアンサー獲得回数32007/09/10 01:56:00

ポイント40pt

> 任意のコードを実行されないようにしたいので、

半分はGEN111さんのと同じですが、以下のような感じでは如何でしょうか?

var Pattern = (function() {
var PatternOfPattern = /^\/(.+)\/([gim]*)$/i;
return function(pattern) {
if((typeof pattern) != "string") {
throw new Error("引数patternは文字列でなければなりません。");
} else if(!PatternOfPattern.test(pattern)) {
throw new Error("引数patternの値は正規表現リテラルの形式を満たしていません。");
}
PatternOfPattern.exec(pattern);
return new RegExp(RegExp.$1, RegExp.$2);
};
})();

//-------------------------------

var regObj1 = Pattern ("/^hoge\\s+.*;$/i");
var regObj2 = new RegExp("^hoge\\s+.*;$", "i");

alert(regObj1 == regObj2); // -> false
alert(regObj1.source == regObj2.source); // -> true

引数patternの文字列自体を取り出せるように拡張することも可能です。

また、new RegExpの部分はわざとtry~catchしていません。これは正規表現としての文法エラーはこのPattern関数内部で隠蔽しないほうが逆によいのでは?と考えたためです。

クロージャを使ったのは、2箇所(test,exec)で使用する変数PatternOfPatternのためで、毎回実行される関数外に書きたいけれど、外から参照・変更されたくないため、使用しました。

id:test_31331

一番ベターな方法のようです。

これでやってみようかと思います。ありがとうございました。

2007/09/10 18:01:40

その他の回答(2件)

id:GEN111 No.1

GEN111回答回数472ベストアンサー獲得回数582007/09/09 22:50:37

ポイント27pt

こういうのはどうでしょう。

var res1 = "/abc/i" ;
var res2 = 'alert("")' ;

var reo1 = eval(String(res1.match(/^\/.*\/[gim]*$/))) ;
alert(reo1 == undefined) ;
var reo2 = eval(String(res2.match(/^\/.*\/[gim]*$/))) ;
alert(reo2 == undefined) ; // 文字列が正規表現にならない場合は undefined
id:test_31331

"/abc/i"の"abc"と"i"を分離させずに正規表現となっているかを正規表現で調べる、という方法ですね。ありがとうございます。

ただ、やっぱりevalで泥臭さが見えるのかも、というのは私の偏見ですかね(笑)

"/abc/i"という表現が、すでに正規表現を表す「記号」としての役割を果たしているので、new RegExp("/abc/i") みたいなもので生成できるように実装されててもいいんじゃないかと思えます。/abc/iがRegExpオブジェクトと振る舞う中で、引数としてRegExpオブジェクトをとるように見えるnew RegExp(/abc/i)はちょっと異質にも見えます。

2007/09/09 23:45:28
id:ofk No.2

ofk回答回数12ベストアンサー獲得回数32007/09/09 23:26:04

ポイント13pt
function getRegExp(exp) {
	try {
		return (new Function('', 'return ' + exp))();
	}
	catch(e) {
		return new RegExp;
	}
}
var a = getRegExp("/abc/i");
alert(a.source);

こんなのはどうでしょうか?

id:test_31331

任意のコードを実行されないようにしたいので、

js> a = getRegExp("alert('abc')")

のようなコードを実行すると、任意のコードが実行できるかたちになってしまいます。

これはちょっと質問に沿わなくなってしまいます。

また、よい方法を思いついたらご回答ください。

ありがとうございます。

2007/09/09 23:55:08
id:Mug No.3

Mug回答回数15ベストアンサー獲得回数32007/09/10 01:56:00ここでベストアンサー

ポイント40pt

> 任意のコードを実行されないようにしたいので、

半分はGEN111さんのと同じですが、以下のような感じでは如何でしょうか?

var Pattern = (function() {
var PatternOfPattern = /^\/(.+)\/([gim]*)$/i;
return function(pattern) {
if((typeof pattern) != "string") {
throw new Error("引数patternは文字列でなければなりません。");
} else if(!PatternOfPattern.test(pattern)) {
throw new Error("引数patternの値は正規表現リテラルの形式を満たしていません。");
}
PatternOfPattern.exec(pattern);
return new RegExp(RegExp.$1, RegExp.$2);
};
})();

//-------------------------------

var regObj1 = Pattern ("/^hoge\\s+.*;$/i");
var regObj2 = new RegExp("^hoge\\s+.*;$", "i");

alert(regObj1 == regObj2); // -> false
alert(regObj1.source == regObj2.source); // -> true

引数patternの文字列自体を取り出せるように拡張することも可能です。

また、new RegExpの部分はわざとtry~catchしていません。これは正規表現としての文法エラーはこのPattern関数内部で隠蔽しないほうが逆によいのでは?と考えたためです。

クロージャを使ったのは、2箇所(test,exec)で使用する変数PatternOfPatternのためで、毎回実行される関数外に書きたいけれど、外から参照・変更されたくないため、使用しました。

id:test_31331

一番ベターな方法のようです。

これでやってみようかと思います。ありがとうございました。

2007/09/10 18:01:40
  • id:ofk
    >>
    "/abc/i"という表現が、すでに正規表現を表す「記号」としての役割を果たしているので、new RegExp("/abc/i") みたいなもので生成できるように実装されててもいいんじゃないかと思えます。
    <<
    そうしてしまうと、/\/abc\/i/で書き表せる正規表現をnew RegExpで生成する方法がなくなってしまいます。
  • id:test_31331
    >>
    そうしてしまうと、/\/abc\/i/で書き表せる正規表現をnew RegExpで生成する方法がなくなってしまいます。
    <<
    new RegExp("/\/abc\/i/")
    という感じでどうでしょうか?
  • id:GEN111
    Number("2*3") も parseInt("2*3") も 6 にはならないし、JavaScript は引数の文字列にはあまり手を入れないのがスタンスなんでしょうね。

    >>
    引数としてRegExpオブジェクトをとるように見えるnew RegExp(/abc/i)はちょっと異質にも見えます。
    <<
    object の代入が参照渡しになってしまうので、それができないとコピー作るのが面倒ですからね。

    >>
    一番ベター
    <<
    それってベスト?
  • id:test_31331
    「これら回答の中ではベスト。ただ、求めていた回答の中では、2番手。」という感じですかね。
    求めていた回答では、デフォルトの関数ひとつで、具体的にいえば、new RegExp("/abc/i")のような感じで
    できるというのがベストでした。
    ただ、今回はそういった方法はやっぱり存在しないようなので、1番はボツ。だから一番ベターだった2番手が
    いるか賞というわけです。

    >>
    object の代入が参照渡しになってしまうので、それができないとコピー作るのが面倒ですからね。
    <<
    駆け出しなもので、いまいち掴めてないですが、
    a = new RegExp("abc")
    b = a
    b == a // => true
    b = new RegExp(a)
    b == a // => false
    あたりのことと想像します。まだそこら辺を意識したことがなかったので、
    そういうこともあったと片隅においておくことにします。ありがとうございます。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません