正则处理

简介: 正则处理

正则处理
正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹
的文本匹配效率低,但是它却更灵活。按照它的语法规则,随需构造出的匹配模式就能够从
原始文本中筛选出几乎任何想你要得到的字符组合。如果你在 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

目录
相关文章
|
缓存 JavaScript API
深入解析API调用:构建现代应用的数据连接纽带
在今天的应用开发中,API调用是构建现代应用的关键组成部分之一。通过API调用,应用程序可以与外部服务通信,获取数据、发送请求并实现各种功能。本博客将深入研究API调用的核心概念、最佳实践以及如何在应用程序中创建强大的数据连接。
595 0
|
Android开发 Kotlin JavaScript
Compose 为什么可以跨平台?
Compose 为什么可以跨平台?
691 0
Compose 为什么可以跨平台?
|
存储 域名解析 缓存
|
3月前
|
NoSQL Java 数据库
Java 全栈学习超全面知识图谱构建完整 Java 知识体系
本文全面讲解Java核心技术体系,涵盖基础语法、面向对象、集合框架、主流框架(Spring、Spring Boot、MyBatis)及三大实战项目(微服务电商、响应式博客、企业后台系统),助你系统掌握Java全栈开发技能。
278 1
|
存储 监控 安全
计算机硬件日常维护
【7月更文挑战第28天】
256 4
|
11月前
|
缓存 监控 安全
检测 Webpack 5 持久化缓存是否存在安全漏洞
【10月更文挑战第23天】通过全面、系统地检测和评估,能够及时发现 Webpack 5 持久化缓存的安全漏洞,并采取有效的措施进行修复,保障项目的安全稳定运行。同时,要持续关注安全技术的发展和变化,不断提升安全检测能力,以应对日益复杂的安全挑战。
|
存储 网络安全 开发工具
Git 中文参考(一)(7)
Git 中文参考(一)
150 2
|
分布式计算 Java Go
Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
436 1
|
关系型数据库 MySQL 数据库
MySQL 启动 登录报错Job for mysqld.service failed because the control process exited with error code. See
MySQL 启动 登录报错Job for mysqld.service failed because the control process exited with error code. See
2016 1