蛇ノ目の記

技術のことも。そうでないことも。

正規表現でURLを処理する話 - グループ化と繰り返し-

URLを正規表現で処理するということをやった。そのメモ的なやつ。

処理の概要

処理したいURLは以下のような構成とする。

https://example.com/users/@{username}/article/{yyyy}/{mm}/{article_id}/

ユーザが入力したURLを受け取って、usernamearticle_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 をよく使っている。

regex101.com

それに加えて、@shimizukawaさんにDebuggex.comを教えてもらった。正規表現が文字列にどうマッチしているか可視化してくれる便利サイト。

https://www.debuggex.com/

実案件での失敗

(?:https?:/{2})?(?:example.com/)? ...のように必ずマッチさせたいURLのホスト部をグループ化した直後で ?に置いていた。

これでは https://users/@{username}/ariticle/{yyyy}/{mm}/{article_id}のような不正なURLがマッチする。他の関数が使っている既存の正規表現を見よう見真似で書いたことが原因の一つ。その正規表現では ?(?:https?:/{2})? のようにスキームに対して使っていた。ここを拡大解釈して、その他の部分でも使ってしまった。

既存のコードを参考にするときは、その意味を自分で咀嚼してから使うべきだと思い知った次第。