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判断key是否在map中的代码
Apr 24 Golang
golang DNS服务器的简单实现操作
Apr 30 Golang
golang 比较浮点数的大小方式
May 02 Golang
Golang之sync.Pool使用详解
May 06 Golang
Go timer如何调度
Jun 09 Golang
Golang生成Excel文档的方法步骤
Jun 09 Golang
Go语言基础知识点介绍
Jul 04 Golang
Go语言并发编程 sync.Once
Oct 16 Golang
Go语言grpc和protobuf
Apr 13 Golang
Golang解析JSON对象
Apr 30 Golang
Go web入门Go pongo2模板引擎
May 20 Golang
GO中sync包自由控制并发示例详解
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
Zend Framework中的简单工厂模式 图文
2012/07/10 PHP
php实现字符串反转输出的方法
2015/03/14 PHP
yii2框架中使用下拉菜单的自动搜索yii-widget-select2实例分析
2016/01/09 PHP
PHP使用curl模拟post上传及接收文件的方法
2016/03/04 PHP
浅析PHP中的闭包和匿名函数
2017/12/25 PHP
Yii 使用intervention/image拓展实现图像处理功能
2019/06/22 PHP
Laravel中validation验证 返回中文提示 全局设置的方法
2019/09/29 PHP
关于jQuery中的end()使用方法
2011/07/10 Javascript
利用jquery的获取JS文件中的字符串内容
2012/02/14 Javascript
JavaScript操作DOM元素的childNodes和children区别
2015/04/01 Javascript
js实现同一页面多个不同运动效果的方法
2015/04/10 Javascript
js实现鼠标点击文本框自动选中内容的方法
2015/08/20 Javascript
AngularJS中的过滤器filter用法完全解析
2016/04/22 Javascript
JavaScript必知必会(七)js对象继承
2016/06/08 Javascript
微信小程序 MINA文件结构
2016/10/17 Javascript
将angular.js项目整合到.net mvc中的方法详解
2017/06/29 Javascript
vue使用mint-ui实现下拉刷新和无限滚动的示例代码
2017/11/06 Javascript
JavaScript 正则命名分组【推荐】
2018/06/07 Javascript
vue cli使用融云实现聊天功能的实例代码
2019/04/19 Javascript
WebStorm无法正确识别Vue3组合式API的解决方案
2021/02/18 Vue.js
Python过滤函数filter()使用自定义函数过滤序列实例
2014/08/26 Python
python读取TXT到数组及列表去重后按原来顺序排序的方法
2015/06/26 Python
Python生成任意范围任意精度的随机数方法
2018/04/09 Python
超简单使用Python换脸实例
2019/03/27 Python
python多进程下实现日志记录按时间分割
2019/07/22 Python
使用python模拟命令行终端的示例
2019/08/13 Python
python框架django项目部署相关知识详解
2019/11/04 Python
python爬虫线程池案例详解(梨视频短视频爬取)
2021/02/20 Python
recorder.js 基于Html5录音功能的实现
2020/05/26 HTML / CSS
世界首屈一指的钓鱼用品商店:TackleDirect
2016/07/26 全球购物
企划专员岗位职责
2013/12/09 职场文书
旅游管理专业生自荐信范文
2014/01/02 职场文书
2014年艾滋病防治工作总结
2014/12/10 职场文书
幼儿园家长工作总结2015
2015/04/25 职场文书
MySQL 数据类型详情
2021/11/11 MySQL
人工智能深度学习OpenAI baselines的使用方法
2022/05/20 Python