9 Go Regexp 正则表达式

一、正则表达式概述

正则表达式(Regular Expression)是一种基于匹配模式的文本处理工具。它有如同一门编程语言一样的模式表示法,赋予使用者描述和分析文本的能力。关于正则表达式,其历史发展和知识体系都非常繁杂,不太可能在此全部展开,如果你对正则表达式有兴趣并愿意深入了解,推荐阅读《精通正则表达式 - Jeffrey E.F Friedl》一书,相信你会得到最全面的理解。在此我们只讲与Go相关的以及使用方式即可。

首先说说正则引擎:

目前的主流正则引擎又分为3类:一、DFA,二、传统型NFA,三、POSIX NFA。

  • 目前使用DFA引擎的程序主要有:awk,egrep,flex,lex,MySQL,Procmail等;

  • 使用传统型NFA引擎的程序主要有:GNU Emacs,Java,ergp,less,more,.NET语言,PCRE library,Perl,PHP,Python,Ruby,sed,vi;

  • 使用POSIX NFA引擎的程序主要有:mawk,Mortice Kern Systems’ utilities,GNU Emacs(使用时可以明确指定);

  • 也有使用DFA/NFA混合的引擎:GNU awk,GNU grep/egrep,Tcl。

以上信息引用自《精通正则表达式》。可见传统NFA使用最为广泛,大多数编程语言都内置NFA引擎实现正则功能。Go与2009年面世,和其他编程语言一样,Go也内置NFA正则引擎。

全面正则表达式语法体系复杂度可比肩一门编程语言,具体使用语法请参考相关教程或工具书,在此只演示一些常用的正则规则:

常用正则规则(其他请详见官方文档):

符号 意义
\d 数字
\D 非数字
\w 单词字符:大小写字母+数字+下划线_
\W 非单词字符
\s 空白字符:\t、\n、\r、\f之一
\S 非空白字符
. 换行符以外的任意字符
\. 一个真正的点
re+ re表示的片段出现1到多次
re* re表示的片段出现0到多次
re? re表示的片段出现0到1次
re{n} re表示的片段出现n次
re{m,n} re表示的片段出现m到n次
re{m,} re表示的片段出现m到无限多次
re{,n} re表示的片段出现0到n次
[abc] a、b、c中间的一个字符
[\s\S] 习惯上表示绝对的任意字符
[a-z] a到z中的任意一个字符
[^abc] 除了abc以外的任意字符
re1|re2 re1或re2所表示的片段
^re$ re片段匹配全文,^匹配字符串开始,$匹配字符串结尾
re*?,re+? re*或re+所代表的片段,使用非贪婪模式
非贪婪模式: re*或re+匹配的字符,越少越好
[a-z]*?http 任意多个小写字母,截止到http出现为止

二、Go 正则表达式的使用

Go标准库提供regexp包支持正则表达式搜索。Go的正则表达式采用RE2语法(除了\c、\C),和Perl、Python等语言的正则基本一致。

https://studygolang.com/pkgdoc Go正则包也提供一些正则语法参考与使用用例,值得一提的是,该包保证正则表达式搜索复杂度为O(n),其中n为输入的长度。这一点很多其他开源实现是无法保证的。虽然在性能上无需担忧,但正则引擎的性能相对于strings包还是较高的,建议日常的字符搜索或替换使用strings包即可,当涉及较复杂的模式匹配时才用正则表达式。

regexp包提供多种正则匹配的方式,四种主要匹配模式的工厂函数:

  • func Compile(expr string) (*Regexp, error)

Compile解析并返回一个正则表达式。如果成功返回,该Regexp就可用于匹配文本。

  • func CompilePOSIX(expr string) (*Regexp, error)

类似Compile但会将语法约束到POSIX ERE(egrep)语法,并将匹配模式设置为leftmost-longest。

  • func MustCompile(str string) *Regexp

MustCompile类似Compile但会在解析失败时panic,主要用于全局正则表达式变量的安全初始化。

  • func MustCompilePOSIX(str string) *Regexp

MustCompilePOSIX类似CompilePOSIX但会在解析失败时panic,主要用于全局正则表达式变量的安全初始化。

返回*Regexp后就可搜索目标文本了,其内部提供多种搜索方法,最常使用的是MustCompile()模式匹配函数,返回一个*Regexp指针,该类型指针提供多种搜索方法,FindAllStringSubmatch()支持搜索所有的全匹配和子模式匹配。其他的请根据需求阅读包文档吧。

以下演示一下常用的手机、邮箱、链接、身份证号码的匹配示例:

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "regexp"
)

//正则表达式演练

const (
    RE_PHONE = `(1[356789]\d)(\d{4})(\d{4})`
    RE_EMAIL = `(\w+?)(\.\w+)?@(\w+)?\.(\w{2,5})(\.\w{2,3})?`
    RE_LINK  = `<a[\s\S]+?href="(http[\s\S]+?)"`

    /*身份证号:4-50121-1970-11-05-5756*/
    //reID = `[1-6]\d{5}-(  (19\d{2})   |   (20((0\d)|(1[0-8])))    )-((0[1-9])|(1[012]))-((0[1-9])|([12]\d)|(3[01]))-\d{3}[\dX]`
    RE_IDENTITY = `[1-6]\d{5}((19\d{2})|(20((0\d)|(1[0-8]))))((0[1-9])|(1[012]))((0[1-9])|([12]\d)|(3[01]))\d{3}[\dX]`
)

func HandleError(where string, err error) {
    if err != nil {
        fmt.Println("发现错误:", err, "「", where, "」")
        os.Exit(1)
    }
}

func GetHtmlContent(url string) (html string) {
    resp, err := http.Get(url)
    HandleError("http.Get", err)
    defer resp.Body.Close()

    bytes, err := ioutil.ReadAll(resp.Body)
    HandleError("ioutil.ReadAll", err)

    return string(bytes)
}

//正则电话号码
func PhoneSpider() {
    //爬取电话号码网页
    htmlForPhone := GetHtmlContent("http://tieba.baidu.com/p/5395331642")

    //正则匹配电话号码
    compilePhone := regexp.MustCompile(RE_PHONE)
    matchPhones := compilePhone.FindAllStringSubmatch(htmlForPhone, -1)

    //输出结果
    for _, v := range matchPhones {
        fmt.Println(v[0])
    }

}

func EmailSpider() {
    //爬取邮箱
    htmlForEmail := GetHtmlContent("https://www.douban.com/group/topic/113790741/")
    htmlForEmail += "fsj.qie@aa.com" //加一下三级域名有点号的邮箱
    htmlForEmail += "sfsaf.rwer@bb.com.cn"

    //正则匹配邮箱
    compileEmail := regexp.MustCompile(RE_EMAIL)
    matchEmail := compileEmail.FindAllStringSubmatch(htmlForEmail, -1)

    //输出结果
    //fmt.Println(matchEmail[0])
    for _, v := range matchEmail {
        fmt.Println(v[0])
    }
}

func IdNumSpider() {
    //爬取身份证号
    htmlForIden := GetHtmlContent("https://www.sohu.com/a/239090484_99956882")
    //正则匹配邮箱
    compileId := regexp.MustCompile(RE_IDENTITY)
    matchId := compileId.FindAllStringSubmatch(htmlForIden, -1)

    //输出结果
    for _, x := range matchId {
        fmt.Println(x[0])
    }
}

//抓取导航网站首页获取链接
func LinksSpider() {
    html := GetHtmlContent("https://www.hao123.com/")
    //fmt.Println(html)

    re := regexp.MustCompile(RE_LINK)
    rets := re.FindAllStringSubmatch(html, -1)
    for _, x := range rets {
        fmt.Println(x[1])
    }
}

至此Go的正则表达式使用就介绍到这,正则表达式是强大的编程工具,一旦你熟悉使用该工具会让你在日常开发中事半功倍,建议继续深入学习正则表达式!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容

  • 作为前端大佬的你,想必对于 JavaScript 的正则表达式非常熟悉了,甚至随手就能利用正则表达式写出一些惊世骇...
    NARUTO_86阅读 25,763评论 1 8
  • 正则表达式到底是什么东西?字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等...
    狮子挽歌阅读 2,148评论 0 9
  • 前言 对于正则,著称火星文字,见名知意主要它晦涩难懂,一直以来,看到它总是怕怕的,在平时,也只是简单的用用,其主要...
    itclanCoder阅读 772评论 0 2
  • 几个正则表达式编辑器 Debuggex :https://www.debuggex.com/ PyRegex:ht...
    没技术的BUG开发攻城狮阅读 4,589评论 0 23
  • 自从年前得空写了两篇文章之后就开始忙了,这一忙就是2个月😭。当时信誓旦旦说的定期写篇博客的计划也就泡汤了🤣,不过好...
    景科同学阅读 1,160评论 0 9