正規表現 先読み後読み 論理積
2019.07.28
この記事は最終更新日から1年以上が経過しています。
どもです。
もうだいぶん前の話となるのですが忘れない為にも備忘録的な感じで。
以前、メンバーから「HTMLのstylesheet要素からパスを取得したい」といった話がありまして、そんな時は正規表現ですよね。
つまり、以下の様なソースから
<link rel="stylesheet" type="text/css" href="./assets/css/style.css">
または、
<link type="text/css" href="./assets/css/style.css" rel="stylesheet">
など、relの位置に関わらず、 hrefの「./assets/css/style.css」といった、パスのみを取得するのです。
で、メンバーが記述したソースは以下の様な感じ。
const matchList: any = line.match(/(?<=rel="stylesheet") href="([0-9a-zA-Z\:\-\.\/_]+)"|href="([0-9a-zA-Z\:\-\.\/_]+)"(?= rel="stylesheet")/);
うーん。間違いではない。
分かるのですがスマートではないですよね。。
正規表現なので、冗長(繰り返し記述)になってしまっているので、短くしたいでよね。
イメージとして、「href=….」 は、後方参照で、 「rel=”style…”」は、^や、 $をうまい使い分けで行けないのかなぁ。
といったイメージでした。(できるかどうかはさておき)
そこで、メンバーからは、「cssのパスのみreplace対象としたかったです。」と言う事。
以下の様に、どこに記述していても取得できるよう。
<link rel="stylesheet" href="./assets/css/style.css"> <link href="./assets/css/style.css" rel="stylesheet">
考えた結果、以下のソースになったと。(でしょうね)
↓
/(?<=rel="stylesheet") href="([0-9a-zA-Z\:\-\.\/_]+)"|href="([0-9a-zA-Z\:\-\.\/_]+)"(?= rel="stylesheet")/
↓
$1 = "./assets/css/style.css"
↓
<link rel="stylesheet" href="./assets/css/style.css?1548407079010">
そもそも、なぜパスを取得するかと言いますと、パスの後ろにタイムスタンプを付与し、キャッシュ無効にする為(ライブラリ)でした。
「ですが、replace内では、”./assets/css/style.css” のみをマッチさせようとしてもうまい正規表現ができませんでした。」
との事。
こんな変な置換とかになるとの事
<link ./assets/css/style.css?1548409972794>
そのため「matchで「 “./assets/css/style.css”」を抽出してからrelpaceを実行した。」
との事。
もうちょいシンプルに行けるはず。とアドバイスしたところ。
「以前のものだと、$1と$2にcssパスが別れて取得が冗長なのを改善しました」
/<link .*href=["']([0-9a-zA-Z\:\-\.\/_]+.css)["']/i)
い、いや linkが増えちゃった。w
違うw
最後投げやりになってしまった感じw
と、レビューするものは、メンバーは「これでOK」と思っているソースに関してレビューし、リファクタリングして良くなりそうなものにはアドバイスが必要となります。
実際に記述するのではなく、「ヒント」を与えることが重要だと思っております。
そこで、意図を理解し、しっかりソースを記述できる人と、できない人で分かれると思うんですよね。
最終的に「できない」と判断した場合、自分が記述するようにしています。
ということで、出先だったので、スマホアプリで記述することに。
後方置換のイメージだったので。
(rel="stylesheet")? href="([0-9a-zA-Z\:\-\.\/_]+)"\s?($1)?
こんな感じなのかな。と想定。
実際にアプリで記述してみたところ、最初にマッチしないと、後方参照は厳しめなのが判明。
とりあえず、これで rel=~ をマッチしつつ、パスをキャプチャ出来るのですが、 rel=~ を上手く扱いたい。。
と、あれやこれやと試していたのですが、そもそも先読み後読みとか、後方参照とか、
ちょっと難しく考えすぎたみたいでした。。。
論理積で解決できることが判明しました。orz
^(?=.*href="([0-9a-zA-Z\:\-\.\/_]+))(?=.*rel="stylesheet")
最終的、「シングルクォート、ダブルクォート」を考慮した正規表現
^(?=.*href=["|']([0-9a-zA-Z\:\-\.\/_]+\.css))(?=.*rel=["|']stylesheet["|'])
もう忘れかけていたので、備忘録です。
用いたiPhoneアプリやWebサービス
iPhone App
RegEx Lab: Regular Expressions
詳細リンク
https://apps.apple.com/us/app/regex-lab-regular-expressions/id1252988123
無料で利用できて良いです。
webサービス
Rubular: a Ruby regular expression editor and tester
Ruby専用なのですが、いくつか利用している中でも、結果後が見やすいので使用してます。
正規表現といえばこの本!
定番といえば定番かもしれませんが、この本を読んで「正規表現」に対する理解が深まった気がします。
この本はおすすめです。
10年前よりかは「得意」にはなった気がしますが、今回書いたような記述がもっとパッと記述できるようにまだまだ勉強不足ですね。。
日々がんばります。
ではではぁ。