TL; DR
-
(?i)
,(?m)
等は、マッチのルールを変更するフラグを表す- 大文字、小文字の区別をなくす/行を跨いでマッチできる等
- グループはキャプチャされない
- リファレンス: https://github.com/google/re2/wiki/Syntax
はじめに
とあるライブラリを調べていたところ、大文字でも小文字でもマッチする謎の正規表現を見つけました。
re := regexp.MustCompile(`(?i)(ab)(c+)`)
fmt.Println(re.FindStringSubmatch("abccc")) // [abccc ab ccc]
// 大文字でもマッチする!?
fmt.Println(re.FindStringSubmatch("AbCcc")) // [AbCcc Ab Ccc]
実際は謎でもなんでもなかったので 調べた内容を備忘録として残しておきます。
正規表現のドキュメント
Goの regexp
パッケージでは、re2の文法が使われています。(pkg.go.devにも説明があります)
大文字、小文字の区別がなくなったのは (?i)
の働きによるものでした。
(?flags)
はフラグを指定する構文で、フラグは正規表現のルールを一時的に変更します。
(?flags)
現在のグループにフラグを設定する;キャプチャされない
フラグの種類
i
冒頭に出てきたフラグ。大文字小文字を区別せずマッチするようになります。
re := regexp.MustCompile(`(?i)(ab)(c+)`)
fmt.Println(re.FindStringSubmatch("abccc")) // [abccc ab ccc]
fmt.Println(re.FindStringSubmatch("AbCcc")) // [AbCcc Ab Ccc]
ちなみに、フラグの説明に「キャプチャされない」とあったように、(?i)
自体は FindStringSubmatch
等の要素に現れません(他のフラグも同様)。特定の文字とマッチするわけではないので、グループの番号がずれないのはありがたいです。
m
^
、$
が行ごとの先頭、末尾とマッチするようになります。
const text = `hoge
piyo
hige
fuga`
// 普通のモードでは、"^" は文字列全体の先頭としかマッチしない
re := regexp.MustCompile(`^h.ge`)
fmt.Println(re.FindAllStringSubmatch(text, -1)) // [[hoge]]
// ?m だと各行とマッチする
re2 := regexp.MustCompile(`(?m)^h.ge`)
fmt.Println(re2.FindAllStringSubmatch(text, -1)) // [[hoge] [hige]]
s
ワイルドカード .
が改行 \n
ともマッチするようになります。行を跨いだマッチが可能です。
const text = `lorem ipsum
dolor
sit
amet`
re := regexp.MustCompile(`(?s)lorem.*amet`)
fmt.Println(re.FindStringSubmatch(text))
// [lorem ipsum
// dolor
// sit
// amet]
U
*
と *?
、+
と +?
の意味が逆になります。(UはungreedyモードのU)
text := `(foo)(bar)(baz)`
// .*は最長マッチ
re := regexp.MustCompile(`\(.*\)`)
fmt.Println(re.FindStringSubmatch(text)) // [(foo)(bar)(baz)]
// .*が最短マッチになる
re2 := regexp.MustCompile(`(?U)\(.*\)`)
fmt.Println(re2.FindStringSubmatch(text)) // [(foo)]
フラグの応用
フラグは複数組み合わせて指定可能です。
const text = `Abc
d
eEEe`
re := regexp.MustCompile(`(?is)ab.*e+`)
fmt.Println(re.FindStringSubmatch(text))
// [Abc
// d
// eEEe]
また、(?flag:<pattern>)
と書くと、かっこの内部だけフラグを有効にできます。
re := regexp.MustCompile(`(?i:ab)c+`)
// かっこの中では大文字小文字を区別しないが
fmt.Println(re.FindStringSubmatch("ABccc")) // [ABccc]
// かっこの外では区別する
fmt.Println(re.FindStringSubmatch("ABCcC")) // []
おわりに
以上、regexpパッケージのフラグの紹介でした。
正規表現は雰囲気で使っていたので、たまにはしっかりドキュメントを読まないとと痛感しました...