1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Goの正規表現 (?<flag>) と和解せよ

Posted at

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パッケージのフラグの紹介でした。
正規表現は雰囲気で使っていたので、たまにはしっかりドキュメントを読まないとと痛感しました... :innocent:

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?