正規表現ユースケース(()によるキャプチャ) : Notion Tips (156)

はじめに

Notion Tips の第156回目も正規表現ユースケースを解説します。今回は ()によるマッチ部分のキャプチャです。言語によって時刻の表示が異なるので、それを正規化します。

言語による時刻の表示の違い

あとから設定で変更することが可能ですが、メンションを作成した時のデフォルトは言語によっていくつかのパターンがありました。

  1. English
    1. 12時間制がデフォルトで H:mm AM または H:mm PM の形で表示されます。
  2. Korean
    1. 12時間制がデフォルトで 오전 H:mm または 오후 H:mm の形で表示されます。
  3. 繁体中文
    1. 12時間制がデフォルトで 上午H:mm または 下午H:mm の形で表示されます。
  4. それ以外
    1. 24時間制がデフォルトで H:mm の形で表示されます。

今回は、計算のためにこれらを 2 と 3 を 1 と同じ形式に正規化する作業を行いました。

() を使ったキャプチャ

今回、H:mm の前にある午前や午後を示す文字列を後ろに移動する必要があります。ここで () を使ったキャプチャを行います。replace や replaceAll では置き換え後にも使いたいマッチ文字列を () でキャプチャし、置換文字列の部分に $1 や $2 を使うことで、複数のキャプチャを取り出すことができます。いつものように全パターンをテストしてみます。

() を使ったキャプチャ

normalize_string の数式は以下のようになりました。

lets(
    name, prop("date string"),
    ifs(
        /* Korean */
        name.test(".*오[전후].*"),
            name
                .replaceAll("오전 (\\d+:\\d+)", "$1 AM")
                .replaceAll("오후 (\\d+:\\d+)", "$1 PM"),
        name.test(".*[上下]午.*"),
            /* 繁体中文 */
            name
                .replaceAll("([年月])", "$1 ")
                .replaceAll("上午(\\d+:\\d+)", "$1 AM")
                .replaceAll("下午(\\d+:\\d+)", "$1 PM"),
        name
    )
)

正規化自体はこれで終わりですが、せっかくなのでこれを日付形式に変換してみましょう。基本的には PM の時だけ 12時間を加算するのですが、12時は0時として取り扱う必要があります。そこで、12 の余りを計算することにします。今回の文字列は日付情報がないので、今日のその時刻に変換してみます。数式は以下のようになりました。

lets(
    str, prop("normalize_string"),
    hasAmPm, str.test("[AP]M"),
    offset, str.test("PM").toNumber() * 12,
    hm, str.match("\d+").map(current.toNumber()),
    h, hasAmPm ? (hm.first() % 12) + offset : hm.first(),
    today().dateAdd(h, "hours").dateAdd(hm.last(), "minutes").formatDate("HH:mm")
)

結果として、以下のように正規化された時刻文字列が作成できました。

正規化された時刻

おわりに

今回は、() を用いたマッチ文字列のキャプチャを解説しました。今回は時間の正規化で解説しましたが、日付の並びを正規化する部分にも利用しています。

hkob.notion.site