Golangの正規表現は少し癖が強いような気がします。
複数行の抜き出しで戸惑ったので基本からおさらい。
ドキュメント:https://golang.org/pkg/regexp/
正規表現:https://github.com/google/re2/wiki/Syntax
ざっくりと関数説明
まずは正規表現を作ります。
- Compile
- CompilePOSIX
- MustCompile
- MustCompilePOSIX
Mustがつくと返値のerrorがなくなり、パース失敗時にpanicします。
POSIXがつくとUNIX共通正規表現でコンパイルされます。
正規表現を作った後はマッチを行います。
- Find系(マッチしたものを抜き出す)
- Allがつく(全てのマッチを返す)
- Indexがつく(インデックスを返す)
- Submatchがつく(マッチしたものと、サブマッチを返す)
- Match系(マッチするかどうか返す:bool)
- Replace系(マッチしたものを置き換える)
- その他
大まかにこんな感じです。
関数サンプル
ベースとしてこんな感じで確かめました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var ( exp *regexp.Regexp str string ) str = `abc def abc ghi` exp = regexp.MustCompile(`abc`) fmt.Printf("%#v\n", exp.MatchString(str)) //true fmt.Printf("%#v\n", exp.FindString(str)) //"abc" fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abc", "abc"} fmt.Printf("%#v\n", exp.ReplaceAllString(str, "XXX")) //"XXX\n\tdef\n\tXXX\n\tghi" |
Submatch
()
でグルーピングされたサブマッチも返します。
1 2 3 4 5 6 7 |
exp = regexp.MustCompile(`a.c`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string{[]string{"abc"}, []string{"abc"}} exp = regexp.MustCompile(`a(.)c`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string{[]string{"abc", "b"}, []string{"abc", "b"}} |
通常の任意文字.
や文字セットなどは検知しません。
改行を含むマッチ
(.*)
でマッチさせるとき、改行を含んでマッチさせるにはsフラグを立てます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
exp = regexp.MustCompile(`a(.*)c`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string{[]string{"abc", "b"}, []string{"abc", "b"}} exp = regexp.MustCompile(`(?s)a(.*)c`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string{[]string{"abc\n\tdef\n\tabc", "bc\n\tdef\n\tab"}} exp = regexp.MustCompile(`ab(.*)bc`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string(nil) exp = regexp.MustCompile(`(?s)ab(.*)bc`) fmt.Printf("%#v\n", exp.FindAllStringSubmatch(str, -1)) //[][]string{[]string{"abc\n\tdef\n\tabc", "c\n\tdef\n\ta"}} |
sフラグを立てない場合に(.*)
が行内のマッチしかしていないことがわかります。
改行に関するものとしてmフラグもあります。
行頭^、行末$を行ごとに判定するためのフラグです。
1 2 3 4 5 6 7 |
exp = regexp.MustCompile(`bc$`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string(nil) exp = regexp.MustCompile(`(?m)bc$`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"bc", "bc"} |
全体ではghiが最後なので1つ目はnil
ですが、行ごとに見ると2つマッチします。
ついでに残りのフラグi
(大小文字区別なし),U
(ものぐさ)はこんな感じです。
1 2 3 4 5 6 7 |
exp = regexp.MustCompile(`(?im)DeF$`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"def"} exp = regexp.MustCompile(`(?sU)a(.*)c`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abc", "abc"} |
ものぐさは常に最短マッチを行うフラグです。
2つ目の例はs
フラグで改行を含んだマッチが可能ですが、U
フラグで(.*)
が(.*?)
と解釈されるため短いマッチabc
が2つの結果になります。
POSIX
詳しい挙動の違いは判りませんがサブマッチの振る舞いが違いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
str = `abcabc` exp = regexp.MustCompile(`abc|abcabc`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abc", "abc"} str = `abcabc` exp = regexp.MustCompile(`abcabc|abc`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abcabc"} exp = regexp.MustCompilePOSIX(`abc|abcabc`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abcabc"} exp = regexp.MustCompilePOSIX(`abcabc|abc`) fmt.Printf("%#v\n", exp.FindAllString(str, -1)) //[]string{"abcabc"} |
通常だと左側優先、POSIXだと最長優先しているようです。