正規表現の世界に足を踏み入れてみた

これまでコピペでなんとかしてきた正規表現ですが、とっつきにくき壁を取っ払って挑戦してみました。
そもそも正規表現とは、文字列をパターンで検索するときに使用する書き方のこと。
エスケープ処理が正規表現のことだと思っていたのは私です🤣

正規表現の基礎まとめ

JSにおいては「正規表現オブジェクト」を通して利用し、2つの書き方がある。

  • 正規表現リテラル
    パターンが変化しない場合に使用すると良パフォーマンス!
    対象範囲を表すデリミタは、JavaScriptの場合は/以外使えない。
sample.js
const regex = /test/;

  • コンストラクタ関数RegExp()
    実行時に正規表現をコンパイルするので、パターンが変化する・不明な時やユーザー入力などから取得するときはこっちを使う。
sample.js
const regex = new RegExp('test');

メソッド

正規表現オブジェクトにはメソッドが格納されており、検索・抽出・置換などが簡単に処理できる。
正規表現 - JavaScript | MDN

オプション(フラグ)

検索モードの変更をする時は、以下のように正規表現の後ろにフラグを記述する。
フラグ種類はどんな順序で記述しても問題ない。

sample.js
const regex = /test/gm;
const regex = new RegExp('test', 'gm');

エスケープ処理

特殊文字を文字として使用するときは、エスケープ処理が必要になる。
特殊文字の前にバックスラッシュ(\)を記述する。

資料によって\だったり¥だったりするが、どっちでもOK

マッチ部分の記憶

マッチした部分を後から再利用したいときには、パターンを( )で囲むことで記憶しておける。(=後方参照)(ちょっとこれは難しい…再勉強する…)

いざ実践!

現在JavaScriptの勉強として、入力されたGoogleDriveの共有リンク情報を書き換えるアプリをつくっているので、その中で正規表現の実践をしてみた記録です。

特定の文字列があるかどうかの判定パターン

  • https://drive.google.comが文頭にある
  • https://drive.google.comの直後に/file/があり、かつ任意の文字列が続く

上記2つの判定パターンは「https://drive.google.com/file/が文頭にあり、かつ任意の文字列が続く」という風に一つにまとめるべきです。
しかし今回の実装ではそれぞれの条件に合わせたバリデーションメッセージを表示させることが目的なので、「/file/ディレクトリがある」というパターンを別に分けています。
なにはともあれ、ググりつつ書いてみる。

肯定先読みというテクニックがあると知って、試しに使ってみました。

sample.js
/^(?=.*drive\.google\.com).*$/;
  1. ^で文頭一致を設定
  2. 肯定先読み(?=…)で指定文字列があるか探す
  3. 指定文字列には「任意の文字列(.)が0個以上(*)ある後に drive.google.comという文字列がある」というものを指定
  4. もし指定文字列がマッチすれば、改めて文頭から文末($)までに任意の文字列(.)が0個以上(*)ある」パターンとマッチするかを検索

つまり「drive.googleがどっかに含まれる文字列」というパターンになります。
改めて見てみるとhttps://の指定は入れた方が確実な気がするし、そうしたら肯定先読み使う必要ないな?と思い、書き直し🤔

sample.js
/^https:\/\/drive\.google\.com.*$/;

思いっきりシンプルになりました。

  1. ^$で文字列全体を設定
  2. https://drive.googleがあって、その後ろに任意の文字列が0個以上(.*)あるか

これでひとまず一つ目のマッチ条件「https://drive.googleが文頭にある」を満たせました。

次は「https://drive.google.comの直後に/file/があり、かつ任意の文字列が続く」というマッチ条件を考えます。
ひとまず/file/の検索だけでパターンを考えてみます。

sample.js
/(?=.*\/file\/).*$/;

これも肯定先読みを使ってパターンを作成してみました。
前項と違う点は文頭指定^がないことだけで、文字列全体のどこかに/file/という指定文字列があるか検索します。
ただこれだと「https://drive.google.comの直後」という条件には当てはまりません🙄
ということで、また肯定先読みはやめて再考。

sample.js
/^https:\/\/drive\.google\.com\/file\/.+$/;
  1. ^$で文字列全体を設定
  2. https://drive.google.com/file/があって、その後ろに任意の文字列が1個以上(.+)あるか

https://drive.google.com/file/以降にもURL情報は続くので、*ではなく+を記述して1個以上の文字の繰り返しという条件を追加しました。

これで「https://drive.google.comが文頭にある」、「https://drive.google.comの直後に/file/があり、かつ任意の文字列が続く」という条件を満たすパターンをそれぞれ作成することができました。

これでバリデーションメッセージの表示条件分岐もばっちりですいえい

特定の文字列間の中身を抽出

続いて文字列を変換させるため、変換箇所のパターンを作成します。

  • https://drive.google.com/file/d/画像ID/view?usp=sharingの画像IDを抽出

つまり/d//viewの間の文字列を抽出したい、ということなので、肯定先読みと肯定後読みを使って表現してみました。

sample.js
/(?<=\/d\/).*?(?=\/view)/;
  1. (?<=\/d\/)/d/が見つかれば、そのあとに任意の文字列が0個以上あるが見る
  2. それがマッチすると/d/の後ろにマッチの基準を置く
  3. そのマッチの基準から/viewが見つかれば、/viewの前にマッチ基準を置く
  4. /d//viewの間の文字列がマッチ!

このとき任意の文字列が0個以上を.*ではなく.*?で表現しているのは、マッチした1個目だけを抽出するためです。(最短一致)
今回の実装内容ではつけなくても問題なさそうだけど念のために👻

もし.*を使用している時に複数の/d//view があった場合(例えばhttps://drive.google.com/file/d/画像ID/view?usp=sharing/d/画像ID/viewみたいな…)、画像ID/view?usp=sharing/d/画像IDとマッチしてしまう。

ということで、簡単なパターンのみの作成にはなっちゃいましたが今回はここまで!

おわり

正規表現がなにものかも理解してない民が、正規表現の世界へ1歩足を踏み入れることができました🙌
ググるといろんな特殊記号がでてきてヴッとなるんですが、簡単なパターンだと初心者でも全然いけました。(たぶん)
これから積極的に正規表現にぶつかっていこうと思います!
正規表現問題サイトも活用してくぞ~~

それでは、 ☁️ぼんっ

参考サイト