URLを正規表現で処理するということをやった。そのメモ的なやつ。
処理の概要
処理したいURLは以下のような構成とする。
https://example.com/users/@{username}/article/{yyyy}/{mm}/{article_id}/
ユーザが入力したURLを受け取って、username
と article_id
を取り出したい。
正規表現の設計
入力されるURLの正規表現の設計として以下の3点を考慮する。
https://
が抜けていてもマッチするhttp://
となっていてもマッチするexample.com
以下は完全一致させる
正規表現
以下が概要・設計に基づいて作った正規表現。
(?:https?:/{2})?example.com/(?:users/@)(\w+)(?:/article/)(?:\d{4}/\d{2})/(\w+)/
正規表現まとめ
参考:
6.2. re — 正規表現操作 — Python 3.6.3 ドキュメント
正規表現 HOWTO — Python 3.6.3 ドキュメント
(?:...)
- グループ化。ただしこのグループにマッチする文字列は後で参照されることがない。
match.groups()
の戻り値に含まれない。今回は正規表現でマッチさせたいが、後から参照することのない部分に用いた。
- グループ化。ただしこのグループにマッチする文字列は後で参照されることがない。
(...)
- グループ化。このグループにマッチする文字列は後から参照することができる。
(match.groups()
の戻り値に含まれる。今回はusername
article_id
を取り出すための用いた。
- グループ化。このグループにマッチする文字列は後から参照することができる。
?
- 繰り返し。直前の正規表現を0回または1回繰り返したものにマッチする。
\w
[a-zA-Z0-9_]
に相当する。
\d
[0-9]
に相当する。
正規表現を書くときは regex101.com
をよく使っている。
それに加えて、@shimizukawaさんにDebuggex.com
を教えてもらった。正規表現が文字列にどうマッチしているか可視化してくれる便利サイト。
実案件での失敗
(?:https?:/{2})?(?:example.com/)? ...
のように必ずマッチさせたいURLのホスト部をグループ化した直後で ?
に置いていた。
これでは https://users/@{username}/ariticle/{yyyy}/{mm}/{article_id}
のような不正なURLがマッチする。他の関数が使っている既存の正規表現を見よう見真似で書いたことが原因の一つ。その正規表現では ?
を (?:https?:/{2})?
のようにスキームに対して使っていた。ここを拡大解釈して、その他の部分でも使ってしまった。
既存のコードを参考にするときは、その意味を自分で咀嚼してから使うべきだと思い知った次第。