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 25 Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
golang通过递归遍历生成树状结构的操作
Apr 28 Golang
Go使用协程交替打印字符
Apr 29 Golang
Golang 编译成DLL文件的操作
May 06 Golang
入门学习Go的基本语法
Jul 07 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
Go归并排序算法的实现方法
Apr 06 Golang
Go获取两个时区的时间差
Apr 20 Golang
Golang ort 中的sortInts 方法
Apr 24 Golang
深入理解 Golang 的字符串
May 04 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将Excel导入数据库及数据库数据导出至Excel的方法
2015/06/24 PHP
Laravel如何创建服务器提供者实例代码
2019/04/15 PHP
laravel 解决Validator使用中出现的问题
2019/10/25 PHP
Javascript匿名函数的一种应用 代码封装
2010/06/27 Javascript
jquery ajax return没有返回值的解决方法
2011/10/20 Javascript
window.parent与window.openner区别介绍
2012/04/12 Javascript
简单实用的全选反选按钮例子
2013/10/18 Javascript
jQuery+css3实现转动的正方形效果(附demo源码下载)
2016/01/27 Javascript
Bootstrap安装环境配置教程分享
2016/05/27 Javascript
python爬取安居客二手房网站数据(实例讲解)
2017/10/19 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
2020/07/22 Javascript
Python多线程编程(五):死锁的形成
2015/04/05 Python
详解python脚本自动生成需要文件实例代码
2017/02/04 Python
浅述python中深浅拷贝原理
2018/09/18 Python
python 弹窗提示警告框MessageBox的实例
2019/06/18 Python
Python中输入和输出(打印)数据实例方法
2019/10/13 Python
Python批量启动多线程代码实例
2020/02/18 Python
实现ECharts双Y轴左右刻度线一致的例子
2020/05/16 Python
Python 如何操作 SQLite 数据库
2020/08/17 Python
python调用有道智云API实现文件批量翻译
2020/10/10 Python
python-地图可视化组件folium的操作
2020/12/14 Python
皇马官方商城:Real Madrid Store
2016/09/02 全球购物
采用专利算法搜索最廉价的机票:CheapAir
2016/09/10 全球购物
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
家庭户外服装:Hawkshead
2017/11/02 全球购物
Contém1g官网:巴西彩妆品牌
2020/01/17 全球购物
文件中有一组整数,要求排序后输出到另一个文件中
2012/01/04 面试题
甜品店的创业计划书范文
2014/01/02 职场文书
工作建议书范文
2014/05/13 职场文书
2014年初中班主任工作总结
2014/11/08 职场文书
2014年综合治理工作总结
2014/11/20 职场文书
个人自荐书范文
2015/03/09 职场文书
酒店财务总监岗位职责
2015/04/03 职场文书
2015年普法依法治理工作总结
2015/05/26 职场文书
开国大典观后感
2015/06/04 职场文书
使用numpy实现矩阵的翻转(flip)与旋转
2021/06/03 Python