Golang 正则匹配效率详解


Posted in Golang onApril 25, 2021

最近有个小需求,校验IMEI是否为15位纯数字(是否合法),以下是正则匹配,与自己实现的简单验证方式进行压测

package main
import (
    "regexp"
    "testing"
)
func BenchmarkIsDigitalRegexp(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = isDigitalRegexp("358901806972417")
    }
}
func BenchmarkIsDigital(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = isDigital("358901806972417")
    }
}
func isDigitalRegexp(imei string) bool {
    if ok, _ := regexp.Match("^[0-9]{15}$", []byte(imei)); ok {
        return true
    }else {
        return false
    }
}
func isDigital(imei string) bool {
    n := len(imei)
    if n == 15 {
        for i := 0; i < n; i++ {
            if imei[i] >= 48 && imei[i] <= 57 {
                continue
            }else {
                return false
            }
        }
    }else {
        return false
    }
    return true
}

压测结果:

C:\Users\M709FJSA\go\src\pprof_demo\re>go test -bench=. -benchmem
goos: windows
goarch: amd64
pkg: pprof_demo/re
BenchmarkIsDigitalRegexp-12       300000              4644 ns/op            6450 B/op         70 allocs/op
BenchmarkIsDigital-12           200000000                9.48 ns/op            0 B/op          0 allocs/op
PASS
ok      pprof_demo/re   4.577s

很明显,正则需要重新分配内存较多,从pprof生成图也可以看出,正则调用关系错综复杂

很明显,正则需要重新分配内存较多,从pprof生成图也可以看出,正则调用关系错综复杂

Golang 正则匹配效率详解

补充:Golang —— 正则表达式

正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹的文本匹配效率低,但是它却更灵活。

按照它的语法规则,随需构造出的匹配模式就能够从原始文本中筛选出几乎任何你想要得到的字符组合。

Go语言通过regexp标准包为正则表达式提供了官方支持,如果你已经使用过其他编程语言提供的正则相关功能,那么你应该对Go语言版本的不会太陌生,但是它们之间也有一些小的差异,因为Go实现的是RE2标准,除了\C。

其实字符串处理我们可以使用strings包来进行搜索(Contains、Index)、替换(Replace)和解析(Split、Join)等操作,但是这些都是简单的字符串操作,他们的搜索都是大小写敏感,而且固定的字符串,如果我们需要匹配可变的那种就没办法实现了,当然如果strings包能解决你的问题,那么就尽量使用它来解决。

因为他们足够简单、而且性能和可读性都会比正则好。

正则匹配规则图

详细请参考官方文档

Golang 正则匹配效率详解

简单的正则表达式

1. 匹配任意类型

buf := "abc azc a7c aac 888 a9c tac"
	// 1. 解释规则
	reg := regexp.MustCompile(`a.c`) // 这里会解析正则表达式,成功就返回解释器(. ——> 除\n外任意字符)
	if reg == nil { // 解释失败
		fmt.Println("MustCompile err")
		return
	}
	// 2. 根据规则提取关键信息
	res :=  reg.FindAllStringSubmatch(buf, -1) //-1表示匹配所有的
	// res :=  reg.FindAllStringSubmatch(buf, 1) //1表示匹配一个
	fmt.Println("res = ", res)

执行结果:

res =  [[abc] [azc] [a7c] [aac] [a9c]]

2. 使用 […] (字符集) 匹配[0-9]之间的数值

buf := "abc azc a7c aac 888 a9c  tac"
 
    //1) 解释规则, 它会解析正则表达式,如果成功返回解释器
    reg1 := regexp.MustCompile(`a[0-9]c`)
 
    if reg1 == nil { //解释失败,返回nil
        fmt.Println("MustCompile err")
        return
    }
 
    //2) 根据规则提取关键信息
    result1 := reg1.FindAllStringSubmatch(buf, -1)
    fmt.Println("result1 = ", result1)

执行结果:

result1 =  [[a7c] [a9c]]

3. 使用 \d 匹配[0-9]之间的数值

buf := "abc azc a7c aac 888 a9c  tac"
 
    //1) 解释规则, 它会解析正则表达式,如果成功返回解释器
    reg1 := regexp.MustCompile(`a\dc`)
    if reg1 == nil { //解释失败,返回nil
        fmt.Println("MustCompile err")
        return
    }
 
    //2) 根据规则提取关键信息
    result1 := reg1.FindAllStringSubmatch(buf, -1)
    fmt.Println("result1 = ", result1)

执行结果:

result1 =  [[a7c] [a9c]]

4.匹配小数

buf := "3.14 456 adsc as23d 1.23 3. 9.99 1lsa23d 0.08 0.00  "
 // 解释正则表达式
 reg := regexp.MustCompile(`\d+\.\d+`) // +表示匹配前一个字符的一次或者多次
 if reg == nil {
  fmt.Println("MustCompile err")
  return
 }
 // 提取关键信息
 res := reg.FindAllStringSubmatch(buf, -1)
 fmt.Println("res = ", res)

执行结果:

res =  [[3.14] [1.23] [9.99] [0.08] [0.00]]

5.匹配信息中某关键字并过滤带标签的

// ` ` 是原生字符串
	buf := `
			<!DOCTYPE html>
			<html lang="zh-CN">
			<head>
				<title>Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国</title>
				<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
				<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
				<meta charset="utf-8">
				<link rel="shortcut icon" href="/static/img/go.ico" rel="external nofollow" >
				<link rel="apple-touch-icon" type="image/png" href="/static/img/logo2.png" rel="external nofollow" >
				<meta name="author" content="polaris <polaris@studygolang.com>">
				<meta name="keywords" content="中文, 文档, 标准库, Go语言,Golang,Go社区,Go中文社区,Golang中文社区,Go语言社区,Go语言学习,学习Go语言,Go语言学习园地,Golang 中国,Golang中国,Golang China, Go语言论坛, Go语言中文网">
				<meta name="description" content="Go语言文档中文版,Go语言中文网,中国 Golang 社区,Go语言学习园地,致力于构建完善的 Golang 中文社区,Go语言爱好者的学习家园。分享 Go 语言知识,交流使用经验">
			</head>
				<div>和爱好</div>
				<div>哈哈
				你在吗
				不在
				</div>
				<div>测试</div>
				<div>你过来啊</div>
			
			<frameset cols="15,85">
				<frame src="/static/pkgdoc/i.html">
				<frame name="main" src="/static/pkgdoc/main.html" tppabs="main.html" >
				<noframes>
				</noframes>
			</frameset>
			</html>
			`
	// 解释正则表达式
	reg := regexp.MustCompile(`<div>(?s:(.*?))</div>`) // s用来处理换行情况
	if reg == nil {
		fmt.Println("MustCompile err")
		return
	}
	// 提取关键字
	res := reg.FindAllStringSubmatch(buf, -1)
	// fmt.Println("res = ", res)
	// 过滤<> </>
	for _, text := range res {
		//fmt.Println("text[0] = ", text[0]) // 带<> </>的
		fmt.Println("text[1] = ", text[1]) //  不带<> </> 的
	}

执行结果:

text[1] =  和爱好
text[1] =  哈哈
    你在吗
    不在
    
text[1] =  测试
text[1] =  你过来啊

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
golang 实现菜单树的生成方式
Apr 28 Golang
Go 实现英尺和米的简单单位换算方式
Apr 29 Golang
golang 接口嵌套实现复用的操作
Apr 29 Golang
goland设置颜色和字体的操作
May 05 Golang
Go语言设计模式之结构型模式
Jun 22 Golang
Go Plugins插件的实现方式
Aug 07 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
golang用type-switch判断interface的实际存储类型
Apr 14 Golang
Golang实现可重入锁的示例代码
May 25 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Go 内联优化让程序员爱不释手
Jun 21 Golang
基于Python实现西西成语接龙小助手
Aug 05 Golang
golang正则之命名分组方式
Apr 25 #Golang
go语言-在mac下brew升级golang
Apr 25 #Golang
go原生库的中bytes.Buffer用法
Apr 25 #Golang
Go缓冲channel和非缓冲channel的区别说明
Apr 25 #Golang
Go语言使用select{}阻塞main函数介绍
win10下go mod配置方式
Go语言-为什么返回值为接口类型,却返回结构体
Apr 24 #Golang
You might like
PHP实现定时生成HTML网站首页实例代码
2008/11/20 PHP
php 无限级缓存的类的扩展
2009/03/16 PHP
生成随机字符串和验证码的类的PHP实例
2013/12/24 PHP
PHP实现的交通银行网银在线支付接口ECSHOP插件和使用例子
2014/05/10 PHP
Android App中DrawerLayout抽屉效果的菜单编写实例
2016/03/21 PHP
php文件后缀不强制为.php的实操方法
2019/09/18 PHP
从新浪弄下来的全屏广告代码 与使用说明
2007/03/15 Javascript
javascript实现上传图片前的预览(TX的面试题)
2007/08/20 Javascript
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
node.js中的querystring.parse方法使用说明
2014/12/10 Javascript
深入理解JavaScript单体内置对象
2016/06/06 Javascript
Bootstrap前端开发案例一
2016/06/17 Javascript
javascript操作cookie
2017/01/17 Javascript
weex里Vuex state使用storage持久化详解
2017/09/09 Javascript
vue中使用vue-router切换页面时滚动条自动滚动到顶部的方法
2017/11/28 Javascript
JavaScript 中的无穷数(Infinity)详解
2020/02/13 Javascript
举例讲解Python的lambda语句声明匿名函数的用法
2016/07/01 Python
[原创]pip和pygal的安装实例教程
2017/12/07 Python
使用Django开发简单接口实现文章增删改查
2019/05/09 Python
python django框架中使用FastDFS分布式文件系统的安装方法
2019/06/10 Python
Python3实现zip分卷压缩过程解析
2019/10/09 Python
Django实现基于类的分页功能
2019/10/31 Python
python读取ini配置的类封装代码实例
2020/01/08 Python
python装饰器三种装饰模式的简单分析
2020/09/04 Python
Numpy(Pandas)删除全为零的列的方法
2020/09/11 Python
python 实现有道翻译功能
2021/02/26 Python
css3教程之倾斜页面
2014/01/27 HTML / CSS
amazeui 验证按钮扩展的实现
2020/08/21 HTML / CSS
Peter Millar官网:美国高档生活服饰品牌
2018/07/02 全球购物
乌克兰机票、铁路和巴士票、酒店搜索、保险:Tickets.ua
2020/01/11 全球购物
意向协议书范本
2014/04/23 职场文书
正科级干部考察材料
2014/05/29 职场文书
质量保证书格式
2015/02/27 职场文书
简历自我评价范文
2019/04/24 职场文书
mongodb的安装和开机自启动详细讲解
2021/08/02 MongoDB
分享mysql的current_timestamp小坑及解决
2021/11/27 MySQL