美文网首页Go进阶系列
9 Go Regexp 正则表达式

9 Go Regexp 正则表达式

作者: GoFuncChan | 来源:发表于2019-07-09 19:54 被阅读0次

    一、正则表达式概述

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

    相关文章

      网友评论

        本文标题:9 Go Regexp 正则表达式

        本文链接:https://www.haomeiwen.com/subject/ptwfkctx.html