golang中的正则表达式使用注意事项与技巧
regexp模块
查看帮助文档:
命令行查看:go doc regexp/syntax
在线查看:
- 中文文档 :
菜鸟教程 介绍一些正则的方法比较详细
GO语言中文文档对一些难懂的基础语法做了解释,比官方文档更易懂 - 英文文档:
https://pkg.go.dev/regexp go标准库文档,有很多示例。
常用的查询方法
常用查询方法
FindAllString
: 完整匹配,仅查询一次,查询到后就返回;FindString
: 完整匹配,可指定查询数量(全部或多个);FindStringSubmatch
:子项查询,获取查询条件中指定的内容。仅查询一次,查询到后就返回;FindAllStringSubmatch
: 子项查询,获取查询条件中指定的内容。可指定查询数量(全部或多个)。
子项查询说明 FindStringSubmatch
注意点:
- 使用英文括号包裹需正则中需要获取的内容
- 这里使用
FindStringSubmatch
方法进行搜索。仅查询一次。
func TestFlag5(t *testing.T) {
str := `_A_123567abv` //要查找的字符串
re, _ := regexp.Compile("_\\w_(.*)") //匹配规则
fmt.Println(re.FindStringSubmatch(str)) //查询
}
输出: 第一个元素永远是全部内容,刚开始不习惯,后来发现还是挺好用的
[_A_123567abv 123567abv]
搜索结果中,第一个是正则中完全匹配到的内容。第二个开始才是想要获取的子项。
子项查询说明 FindAllStringSubmatch
分组查询时,在正则表达式中,将需要查询出来的内容用()
括起来。
//查询内容
data1 := "type Demo struct {
Name string `json:\"name\" form:\"name\" gorm:\"column:name;comment: \"`
Age int `json:\"age\" form:\"age\" gorm:\"column:age;comment: \"`
}"
//正则表达式:查询结构体字段、字段类型和json标签中的内容
grep, _ := regexp.Compile(`([\w]{1,})[ ]{1,}([\[\]\w]{2,})[ ]{1,}` + "`" + `(json):"([\w]{1,})[,]{0,}(\w{0,})\"` + ".*`")
//使用FindAllStringSubmatch函数查询
result := grep.FindAllStringSubmatch(*s.StructValue, -1)
输出结果:
[
[Name string `json:"name" form:"name" gorm:"column:name;comment: "` Name string json name ] [Age int `json:"age" form:"age" gorm:"column:age;comment: "` Age int json age ]
]
注意,在这里,每个匹配的搜索结果都单独放在一个切片中。
常见问题与注意事项
反引号中使用类似\w
(\w
代表0-9A-Za-z_)这样的语法不用做任何处理
grep, _ := regexp.Compile(`\w{1,}`)
而在双引号中,反斜杠代表转义符。所以使用\w
时,需要额外使用一个\
将反斜杠转义为普通字符串,否则报错
grep, _ := regexp.Compile("\\w{1,}")
因为url中可能包含特殊字符,所以需要提前对它进行转义。像这样:
re,_ := regexp.Compile(regexp.QuoteMeta(oldStr))
关于最多和最少匹配
python中,最小匹配是有函数可以调用的。而go的regexp模块没有最小匹配的函数。
如果要使用最小匹配,需要配合正则的语法,在文档中介绍在这里
重复:
x* 重复>=0次匹配x,越多越好(优先重复匹配x)
x+ 重复>=1次匹配x,越多越好(优先重复匹配x)
x? 0或1次匹配x,优先1次
x{n,m} n到m次匹配x,越多越好(优先重复匹配x)
x{n,} 重复>=n次匹配x,越多越好(优先重复匹配x)
x{n} 重复n次匹配x
x*? 重复>=0次匹配x,越少越好(优先跳出重复)
x+? 重复>=1次匹配x,越少越好(优先跳出重复)
x?? 0或1次匹配x,优先0次
x{n,m}? n到m次匹配x,越少越好(优先跳出重复)
x{n,}? 重复>=n次匹配x,越少越好(优先跳出重复)
x{n}? 重复n次匹配x
上面的解释中,带有越少越好的就是最小匹配。*
和+
稍有不慎就把全文都给你匹配出来
str := `"老虎大王者荣耀超人"` //要查找的字符串
re := regexp.MustCompile("[\u4e00-\u9fa5]{1,3}") //匹配规则,匹配1到3个中文字符
fmt.Println(re.FindAllString(str, -1)) //查询
输出
[老虎大 王者荣 耀超人]
关于.*? 的坑
在javaScript和python等其它语言中,.*?
表示查询所有任意的字符串。
而在golang中,文档中是这样说明的
x*? 重复>=0次匹配x,越少越好(优先跳出重复)
注意,重点在于匹配原则是内容越少越好,而正则表达式.*?
中的.
代表任意字符串(包括空字符串)。
按照这种匹配原则,空字符串总是优先匹配到的,这样就导致了下面的问题:
func TestFlag5(t *testing.T) {
data := "你吃饭了吗"
grep, _ := regexp.Compile(".*?")
result := grep.FindAllString(data, -1)
t.Log(result)
}
输出结果是
structToJson_test.go:107: [ ]
它只匹配到了五个空字符串。
解决方案有两种。第一种,把问号去掉,变成.*
即可。 查看文档,(.*
的匹配原则是越多越好)
func TestFlag6(t *testing.T) {
data1 := "你吃饭了吗"
grep, _ := regexp.Compile(".*")//.*?改成了 .*
result := grep.FindAllString(data1, -1)
t.Log(result)
}
输出
structToJson_test.go:107: [你吃饭了吗]
第二种方案,.*?
后面必须包含字符串
func TestFlag7(t *testing.T) {
str := `你吃饭了吗` //要查找的字符串
re, _ := regexp.Compile(".*?了吗") //.*?后面加几个字符串,开头加不加无所谓
t.Log(re.FindAllString(str, -1)) //查询
}
输出
[你吃饭了吗]
关于匹配中文字符的坑
匹配中文字符这里有个坑,如果匹配规则使用的是双引号,正常输入匹配规则即可
re := regexp.MustCompile("[\u4e00-\u9fa5]{1,}")//匹配规则,\u4e00-\u9fa5表示unicoce中的人一个中文字符
但是如果你使用反引号包裹上面的匹配规则,查询时会导致panic报错
re := regexp.MustCompile(`[\u4e00-\u9fa5]{1,}`)//错误示例
需要写一个双引号包裹的匹配规则,然后赋值粘贴进去,最后变成一串乱码,这才是正确的。
re := regexp.MustCompile(`[一-龥]{1,}`) //匹配规则