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 实现对Map进行键值自定义排序
Apr 28 Golang
Go语言 go程释放操作(退出/销毁)
Apr 30 Golang
golang 比较浮点数的大小方式
May 02 Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 Golang
go语言基础 seek光标位置os包的使用
May 09 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
基于Go语言构建RESTful API服务
Jul 25 Golang
Golang MatrixOne使用介绍和汇编语法
Apr 19 Golang
Golang jwt身份认证
Apr 20 Golang
Go 内联优化让程序员爱不释手
Jun 21 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
destoon复制新模块的方法
2014/06/21 PHP
ThinkPHP视图查询详解
2014/06/30 PHP
微信公众平台接口开发入门示例
2014/12/24 PHP
详解HTTP Cookie状态管理机制
2016/01/14 PHP
教你在header中隐藏php的版本信息
2016/08/10 PHP
Yii2中多表关联查询hasOne hasMany的方法
2017/02/15 PHP
PHP 实现人民币小写转换成大写的方法及大小写转换函数
2017/11/17 PHP
PHP封装curl的调用接口及常用函数详解
2018/05/31 PHP
Smarty模板语法详解
2019/07/20 PHP
js png图片(有含有透明)在IE6中为什么不透明了
2010/02/07 Javascript
JS和函数式语言的三特性
2014/03/05 Javascript
简单的jquery左侧导航栏和页面选中效果
2014/08/21 Javascript
jquery实现ajax加载超时提示的方法
2016/07/23 Javascript
jquery处理checkbox(复选框)是否被选中实例代码
2017/06/12 jQuery
JavaScript框架Angular和React深度对比
2017/11/20 Javascript
vue视频播放插件vue-video-player的具体使用方法
2019/11/08 Javascript
微信小程序在text文本实现多种字体样式
2019/11/08 Javascript
Vue 中 a标签上href无法跳转的解决方式
2019/11/12 Javascript
python采用getopt解析命令行输入参数实例
2014/09/30 Python
Python中特殊函数集锦
2015/07/27 Python
Django之创建引擎索引报错及解决详解
2019/07/17 Python
对django 模型 unique together的示例讲解
2019/08/06 Python
python爬虫 基于requests模块发起ajax的get请求实现解析
2019/08/20 Python
Python中logging日志库实例详解
2020/02/19 Python
Django数据库操作之save与update的使用
2020/04/01 Python
Styleonme中文网:韩国高档人气品牌
2017/06/21 全球购物
SQL Server 2000数据库的文件有哪些,分别进行描述。
2015/11/09 面试题
光荣入党自我鉴定
2014/01/22 职场文书
电大本科自我鉴定
2014/02/05 职场文书
2014道德模范事迹材料
2014/02/16 职场文书
医学生毕业自我鉴定
2014/03/26 职场文书
庆七一主持词
2015/06/29 职场文书
班级班风口号大全
2015/12/25 职场文书
《浅水洼里的小鱼》教学反思
2016/02/16 职场文书
CSS3通过var()和calc()函数实现动画特效
2021/03/30 HTML / CSS
javascript实现计算器功能详解流程
2021/11/01 Javascript