@TaikiTkwkbysh (WAKA Engineer)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Javascriptの正規表現について

解決したいこと

javascriptを学習しているものです。
現在参考書にて、正規表現でURLを取得する事をしているのですが、
わからずに悩んでいる状況です。

問題の箇所

該当するソースコード

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

let str = "サポートサイトはhttp://www.wings.msn.to/です。";

let result = str.match(p);
for(let i=0; len=result.length; i<len; i++){
  console.log(result[i]);
}

//結果
http://www.wings.msn.to/
undefined
msn.
/

確認したいこと

今回の出力結果のサブマッチ文字列、

http://www.wings.msn.to/
undefined
msn.
/

なのですが、この出力がそれぞれどの箇所に対応しているのかがわからない為、
確認したい所存でございます。
自分の認識としましては、

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

undefined  //  ⇨ (s)がない為 

msn.      // ⇨  ([\w-]+\.)の処理で、www.wings.msn.の最後のマッチ

/         // ⇨ (\/[\w- ./?%&=]*)? の箇所の丸カッコ()内の直前のスラッシュ

という風に認識しております。
また、変数pで宣言している正規表現において、一部何を表現しているのかが曖昧なので、そこもご教示頂けますと幸いでございます。

※現在自分は現在、下記のように解釈しております。
(説明が長くなっております。申し訳ありません)

//(本文)
let p = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?/g;

//(抜粋)
let p = /*http(s)?:\/\/*/([\w-]+\.)+/*[\w-]+(\/[\w- ./?%&=]*)?/g;*/

//問題箇所
([\w-]+\.)+

1:[\w-]→対象は英数字、アンダーバー、ハイフン。[]があるので三種のいずれか1文字がマッチする。

2:その後に + があるので、英数字、アンダーバー、ハイフンでできた1文字以上の文字列がマッチするようになる。結果、www,wings,msnがマッチする。

3:その後に \. があるので、②の処理の後にピリオドがつく文字列がマッチする。

4:1〜3の処理が丸カッコ()で囲われていて、その後に + があるので、
英数字、アンダーバー、ハイフンでできている且つ、後にピリオドがつく1文字以上の文字列が1回以上マッチするようになる。
結果、www. , wings. , msn. の3つがこの①の文で全てマッチする。

//(抜粋)
let p = /*http(s)?:\/\/([\w-]+\.)+*/[\w-]+/*(\/[\w- ./?%&=]*)?/g;*/

//問題箇所
[\w-]+

1:①の処理で末にピリオドが付かない文字列が出たので、②に移動。
ここでは、①の1、2同様、英数字、アンダーバー、ハイフンでできた1文字以上の文字列がマッチする。結果、toがマッチする。

//(抜粋)
let p = /*http(s)?:\/\/([\w-]+\.)+[\w-]+*/(\/[\w- ./?%&=]*)?/g;

//問題箇所
(\/[\w- ./?%&=]*)?

1:②でマッチする文字がなくなったので、③に移動。まず初めにスラッシュがある。結果、toの後のスラッシュがここでマッチする。

2:スラッシュ以降では、英数字、アンダーバー、ハイフン、ピリオド、スラッシュ、?、%、&、=が対象。それが[]で囲まれているので、どれか1文字。その後に*があるので、0文字以上マッチする。

3:2の処理が丸カッコ()で囲われていて、その後に?があるので、スラッシュかつ、英数字、アンダーバー、ハイフン、スラッシュ、?、%、&、=のいずれか0文字以上の文字列を0もしくは1文字でマッチする。

1 likes

3Answer

ご質問のコードは間違っていませんか?

let result = str.match(p);
for(let i=0; len=result.length; i<len; i++){

の部分を

let result = p.exec(str);
for(let i=0; i<result.length; i++){

にしないとその結果にはなりません。

さておき、サブマッチ文字列の対応の認識はそれで合っています。正規表現の解釈も正しいです。気になるところといえば

2:その後に + があるので、英数字、アンダーバー、ハイフンでできた1文字以上の文字列がマッチするようになる。結果、www,wings,msnがマッチする。

で「www,wings,msnがマッチする」と書いてあることくらいですね。その後の \. も含めて考えないと、この時点ではマッチするともしないとも言えないので。ともかくその下の

結果、www. , wings. , msn. の3つがこの①の文で全てマッチする。

は正しいので大丈夫でしょう。

もう1点、 [\w- ./?%&=] のハイフンはちょっと紛らわしいですね。というのも、文字クラス [] の中間にあるハイフンは通常は文字の範囲を表すからです。たとえば [a-z] は「a かハイフンか z のどれか1文字」ではなく、「a から z の範囲の小文字アルファベットどれか1文字」を表します。ところが \w- はどうかというと、範囲のように見えて \w が特定の文字ではないために範囲とみなされず、「\w かハイフンか半角スペース」になるようです。

ややこしいので、文字クラスに文字通りのハイフンを含めるときはバックスラッシュを前置して [\w\- ./?%&=] と書くか、またはハイフンを最後に置いて [\w ./?%&=-]とするのが無難です。

1Like

@uasi
この度はご教示頂き、誠にありがというございます。

まずは自分の認識の成否及び細かい訂正を頂き、誠にありがとうございます。
かなり長文で記載をしてしまったにも関わらずご確認をして頂けて、大変ありがたい限りでございます。

特に[\w- ./?%&=]の解説に致しましては、自分も[]内のハイフンの役割が分かりづらく困っていた為、記載をし忘れていましたが大変スッキリしました。

次に、ご質問いただいたjavascriptのコードの箇所についてですが、私の記載に誤りがありました。
誤りのある箇所は下記の通りです。

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

let result = str.match(p);

for(let i=0, len=result.length; i<len; i++){
  console.log(result[i]);
}

正規表現のオプションがgではなく、iでした。
で、for文のlet i=0の後ろはセミコロンではなく、カンマでした。
自分もi<result.length;の方がわかりやすいのですが、参考書曰く、
「ループの都度プロパティにアクセスしなければならないので、性能が劣化する」との事で、
参考書内ではfor文は基本この書き方となっております。
それ以外は参考書通りでございます。
今回見ている参考書は、「改訂新版 javascript本格入門」で、正誤表には特に訂正はございませんでした。

0Like

正規表現のオプションがgではなく、iでした。

なるほど。 str.match(p) の返り値は g フラグの有無で変わります。 i なら問題なさそうです。

「ループの都度プロパティにアクセスしなければならないので、性能が劣化する」との事で、
参考書内ではfor文は基本この書き方となっております。

どうですかね……プロパティアクセスにかかる時間はせいぜいナノ秒単位なので数百万回はループしないと違いは表れないでしょうし、その頃には JIT で最適化がかかって速くなる可能性もあります。あまり気にしなくていいと思います。もちろん、長さを求める処理が result.length より遥かに時間がかかるような場合には、先に一度だけ計算しておくのはいい考えです。

0Like

Comments

  1. @TaikiTkwkbysh

    Questioner

    @uasi様

    ご連絡頂き、誠にありがとうございます。
    記載ミス、申し訳ございません。ご迷惑お掛け致しました。

    参考書を読み進めた際、今回の問題のページの次のページにご教示頂いた
    let result = p.exec(str);
    のコードも出てきました。

    gオプションがついている状態ではこの結果に確かになりませんね、、、、
    申し訳ございません。
    いつもご教示頂き、誠にありがとうございます。

    今後とも何卒ご教示の程、宜しくお願い申し上げます。

Your answer might help someone💌