正则处理

简介: 正则处理

正则处理
正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹
的文本匹配效率低,但是它却更灵活。按照它的语法规则,随需构造出的匹配模式就能够从
原始文本中筛选出几乎任何想你要得到的字符组合。如果你在 Web 开发中需要从一些文本
数据源中获取数据,那么你只需要按照它的语法规则,随需构造出正确的模式字符串就能够
从原数据源提取出有意义的文本信息。
Go 语言通过 regexp 标准包为正则表达式提供了官方支持,如果你已经使用过其他编程语
言提供的正则相关功能,那么你应该对 Go 语言版本的不会太陌生,但是它们之间也有一
些小的差异,因为 Go 实现的是 RE2 标准,除了\C,详细的语法描述参考:
http://code.google.com/p/re2/wiki/Syntax
其实字符串处理我们可以使用 strings 包来进行搜索(Contains、Index)、替换(Replace)和解
析(Split、Join)等操作,但是这些都是简单的字符串操作,他们的搜索都是大小写敏感,而
且固定的字符串,如果我们需要匹配可变的那种就没办法实现了,当然如果 strings 包能解
决你的问题,那么就尽量使用它来解决。因为他们足够简单、而且性能和可读性都会比正则
好。
如果你还记得,在前面表单验证的小节里,我们已经接触过正则处理,在那里我们利用了
它来验证输入的信息是否满足某些预设的条件。在使用中需要注意的一点就是:所有的字符
都是 UTF-8 编码的。接下来让我们更加深入的来学习 Go 语言的 regexp 包相关知识吧。
通过正则判断是否匹配
regexp 包中含有三个函数用来判断是否匹配,如果匹配返回 true,否则返回 false
func Match(pattern string, b []byte) (matched bool, error error)
func MatchReader(pattern string, r io.RuneReader) (matched bool, error error)
func MatchString(pattern string, s string) (matched bool, error error)
上面的三个函数实现了同一个功能,就是判断 pattern 是否和输入源匹配,匹配的话就返回
true,如果解析正则出错则返回 error。三个函数的输入源分别是 byte slice、RuneReader 和
string。
如果要验证一个输入是不是 IP 地址,那么如何来判断呢?请看如下实现
func IsIP(ip string) (b bool) {
if m, _ := regexp.MatchString("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.
[0-9]{1,3}$", ip); !m {
return false
}
return true
}
可以看到,regexp 的 pattern 和我们平常使用的正则一模一样。再来看一个例子:当用户输
入一个字符串,我们想知道是不是一次合法的输入:
func main() {
if len(os.Args) == 1 {
fmt.Println("Usage: regexp [string]")
os.Exit(1)
} else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m {
fmt.Println("数字")
} else {
fmt.Println("不是数字")
}
}
在上面的两个小例子中,我们采用了 Match(Reader|String)来判断一些字符串是否符合我
们的描述需求,它们使用起来非常方便。
通过正则获取内容
Match 模式只能用来对字符串的判断,而无法截取字符串的一部分、过滤字符串、或者提取
出符合条件的一批字符串。如果想要满足这些需求,那就需要使用正则表达式的复杂模式。
我们经常需要一些爬虫程序,下面就以爬虫为例来说明如何使用正则来过滤或截取抓取到
的数据:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
)
func main() {
resp, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("http get error.")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("http read error")
return
}
src := string(body)
//将 HTML 标签全转换成小写
re, _ := regexp.Compile("\<[\S\s]+?\>")
src = re.ReplaceAllStringFunc(src, strings.ToLower)
//去除 STYLE
re, _ = regexp.Compile("\<style[\S\s]+?\\>")
src = re.ReplaceAllString(src, "")
//去除 SCRIPT
re, _ = regexp.Compile("\<script[\S\s]+?\\>")
src = re.ReplaceAllString(src, "")
//去除所有尖括号内的 HTML 代码,并换成换行符
re, _ = regexp.Compile("\<[\S\s]+?\>")
src = re.ReplaceAllString(src, "\n")
//去除连续的换行符
re, _ = regexp.Compile("\s{2,}")
src = re.ReplaceAllString(src, "\n")
fmt.Println(strings.TrimSpace(src))
}
从这个示例可以看出,使用复杂的正则首先是 Compile,它会解析正则表达式是否合法,
如果正确,那么就会返回一个 Regexp,然后就可以利用返回的 Regexp 在任意的字符串上
面执行需要的操作。
解析正则表达式的有如下几个方法:
func Compile(expr string) (*Regexp, error)
func CompilePOSIX(expr string) (*Regexp, error)
func MustCompile(str string) *Regexp
func MustCompilePOSIX(str string) *Regexp
CompilePOSIX 和 Compile 的不同点在于 POSIX 必须使用 POSIX 语法,它使用最左最长
方式搜索,而 Compile 是采用的则只采用最左方式搜索(例如[a-z]{2,4}这样一个正则表达式,
应用于"aa09aaa88aaaa"这个文本串时,CompilePOSIX 返回了 aaaa,而 Compile 的返回
的是 aa)。前缀有 Must 的函数表示,在解析正则语法的时候,如果匹配模式串不满足正确
的语法则直接 panic,而不加 Must 的则只是返回错误。
在了解了如何新建一个 Regexp 之后,我们再来看一下这个 struct 提供了哪些方法来辅助
我们操作字符串,首先我们来看下面这写用来搜索的函数:
func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllString(s string, n int) []string
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
func (re *Regexp) FindString(s string) string
func (re *Regexp) FindStringIndex(s string) (loc []int)
func (re *Regexp) FindStringSubmatch(s string) []string
func (re *Regexp) FindStringSubmatchIndex(s string) []int
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindSubmatchIndex(b []byte) []int
上面这 18 个函数我们根据输入源(byte slice、string 和 io.RuneReader)不同还可以继续简化
成如下几个,其他的只是输入源不一样,其他功能基本是一样的:
func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindSubmatchIndex(b []byte) []int
对于这些函数的使用我们来看下面这个例子
package main
import (
"fmt"
"regexp"
)
func main() {
a := "I am learning Go language"
re, _ := regexp.Compile("[a-z]{2,4}")
//查找符合正则的第一个
one := re.Find([]byte(a))
fmt.Println("Find:", string(one))
//查找符合正则的所有 slice,n 小于 0 表示返回全部符合的字符串,不然就是返回指定的长度
all := re.FindAll([]byte(a), -1)
fmt.Println("FindAll", all)
//查找符合条件的 index 位置,开始位置和结束位置
index := re.FindIndex([]byte(a))
fmt.Println("FindIndex", index)
//查找符合条件的所有的 index 位置,n 同上
allindex := re.FindAllIndex([]byte(a), -1)
fmt.Println("FindAllIndex", allindex)
re2, _ := regexp.Compile("am(.)lang(.)")
//查找 Submatch,返回数组,第一个元素是匹配的全部元素,第二个元素是第一个()里面的,第三个
是第二个()里面的
//下面的输出第一个元素是"am learning Go language"
//第二个元素是" learning Go ",注意包含空格的输出
//第三个元素是"uage"
submatch := re2.FindSubmatch([]byte(a))
fmt.Println("FindSubmatch", submatch)
for _, v := range submatch {
fmt.Println(string(v))
}
//定义和上面的 FindIndex 一样
submatchindex := re2.FindSubmatchIndex([]byte(a))
fmt.Println(submatchindex)
//FindAllSubmatch,查找所有符合条件的子匹配
submatchall := re2.FindAllSubmatch([]byte(a), -1)
fmt.Println(submatchall)
//FindAllSubmatchIndex,查找所有字匹配的 index
submatchallindex := re2.FindAllSubmatchIndex([]byte(a), -1)
fmt.Println(submatchallindex)
}
前面介绍过匹配函数,Regexp 也定义了三个函数,它们和同名的外部函数功能一模一样,
其实外部函数就是调用了这 Regexp 的三个函数来实现的:
func (re *Regexp) Match(b []byte) bool
func (re *Regexp) MatchReader(r io.RuneReader) bool
func (re *Regexp) MatchString(s string) bool

目录
相关文章
|
1月前
|
数据安全/隐私保护
正则表达式小结
正则表达式小结
38 0
|
13天前
|
C++
正则表达式
正则表达式
|
1月前
使用正则表达式
使用正则表达式。
41 1
|
7月前
正则表达式汇总
正则表达式汇总
29 0
|
数据安全/隐私保护
正则表达式_2
学习自编程胶囊课程。
92 1
正则表达式_2
|
前端开发 数据安全/隐私保护 Windows
常用正则表达式
开发中经常使用的正则表达式
128 0
最全的常用正则表达式大全
很多不太懂正则的朋友,在遇到需要用正则校验数据时,往往是在网上去找很久,结果找来的还是不很符合要求。所以我最近把开发中常用的一些正则表达式整理了一下,在这里分享一下。
1349 0
|
数据安全/隐私保护
正则表达式总结
正则表达式 定义: 正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
1224 0
|
Web App开发 JavaScript 前端开发
正则表达式小记
什么是正则表达式 正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、replace、search 和 split 方法。
1192 0