
正则表达式快速入门
应用场景:有关字符串的搜索,替换,验证,过滤 正则表达式语言是一种内置于程序设计语言中的一种迷你语言
演示实例将使用 JS 和 Go 来进行展示 不同语言对正则的实现会有所区别
如何让匹配更精准? 制定更严格的模式串来约束匹配结果,使得我们不会匹配到一些不想要的结果
同一匹配目的但实现有区别
匹配多个
js 中使用 g 的标志来匹配多个
go 中使用 FindAll 的方法来匹配多个
大小写
js 中使用 i 的标志位来忽略大小写
go 中需要在模式串前加 (?i)
匹配任意字符
. 匹配任意单个字符,如果要单独匹配 . 字符就需要转义成 \.
匹配一组字符
使用元字符 [ 和 ] 来定义字符集合
如果里面只有字母,则表示单个字符匹配集合中的任意一个字符,例如 [ab] 表示匹配 a 字母开头或 b 字母
使用这种方式也可以达到局部不区分大小写的匹配效果,如 [Aa]
除了一个个枚举字符区间,正则提供了 - 连字符来简化区间的表达,例如 [0-9] [A-Z] 等,连字符只有在 [] 中才有特殊意义,多个连字符表示的区间可以拼接在一起,例如 [A-Za-z0-9]
使用 ^ 可以对字符区间进行取非匹配,例如 除数字以外的字符 [^0-9]
如果要匹配 + 和 . 在区间中不需要转义
单独一个字符也可以写作字符集合,在后面有加号或者星号的时候能增加可读性
元字符
包括 . [ ] \ + * ? { } ( ) |
如果要匹配这些字符需要加上 \ 进行转义
还有匹配空白字符的空白元字符 [\b] \f \n \r t \v
匹配特定字符类别
这些表示字符类别的元字符能够简化区间的表达从而简化整个模式串
\d 等价于 [0-9]
\D 等价于 [^0-9] 一般来说大写字母表示取非
\w 表示 任何一个无大小写差别字母数字或下划线
\W 同理可得
匹配空白字符
\s 任何一个空白字符,等价于 [\f\n\r\t\v]
\S 同理可得
匹配多个字符
只需要在字符集合的后面附上一个 + 号,就可以按照同样的规则匹配至少一个字符
如果使用 * 号,则可以满足无字符的情况,表示匹配0个或多个字符
匹配0个或1个
后接 ? 可以匹配0个或者1个这个字符来表示可选匹配,例如 https? 既能匹配 http 又能匹配 https
限定匹配的重复次数
因此引入 { 和 } 元字符来限制匹配次数
重复确定的值:{3} 表示前一个字符重复3次算一次匹配
例如:#[[:xdigit]]{6} 用于匹配色值
重复一个范围内的次数:{最小值,最大值}
最小值可以是0,? 相当于 {0,1}
至少重复 k 次:{k,}
贪婪型和懒惰型元字符
默认的 + + {n, } 都是贪婪型元字符,它们会尝试匹配尽量多的字符,在目标串中有模式串的子串嵌套的时候贪婪模式会不适用,本来能得到多个目标串的却只能得到一个
懒惰型元字符会尝试匹配尽量少的字符,启用懒惰模式只需要在贪婪型元字符后加上 ? 即可
例如匹配超链接标签 <a href=".*?"><\/a>
位置匹配
用来确定在什么地方进行匹配操作,限定匹配的位置
边界限定符
一些特殊的元字符可以指定匹配操作在什么位置发生
单词边界 \b 用来匹配一个单词的开始或结尾,本质上是 \w 和 \W 之间
例如 \bbuild\b 就不会匹配到 building
如果要匹配一个完整的单词,前后都必须加上 \b ,如果只是匹配以某串开始或结尾就只需要在对应的位置上加上 \b 即可
同理 \B 匹配非单词边界
字符串边界 ^ 在字符集合外匹配字符串的开头而 $ 匹配字符串的末尾
例如:匹配 xml 文档的开头标签 ^\s*<\?xml .*?\?> ,\s* 表示允许开头可以有0个或者多个空白字符
分行匹配模式
最开头加上 (?m) 能够开启分行匹配模式,^ 不仅可以匹配正常字符串的开头,还能够匹配换行后的开始位置
需要了解具体实现是否支持此模式
子表达式
把一个表达式划分成多个子表达式,并用 () 围起来,这使得这些子表达式能够被当做一个独立的元素(字符)来使用,即便是短语也可以当做一个字符来进行处理
例如:简单匹配IP地址 (\d{1,3}\.){3}\d{1,3}
先匹配三个连续的 xxx. 然后再匹配最后一个 xxx
为了提高可读性可以给每一个子表达式都使用括号,但是不同的实现可能会导致匹配性能的下降
串的集合匹配可以使用 (串1|串2|串3) 的方式来进行匹配,| 表示或
子表达式嵌套
对于严格定义的 IP 地址来说,需要使用多个表达式嵌套的方式来进行更严格的匹配
考虑数字的位数以及开头的数字的要求,不难写出这样的嵌套表达式
^(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.)(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){2}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))$
回溯引用
这允许了模式串引用前面的匹配结果,达到某种程度上的前后一致匹配
通过 \N 的方式,我们可以直接引用模式串中的第 N 个子表达式,N从1开始,使得我们的匹配产生一定的关联性。子表达式需要用圆括号括起来,如果N为0,那么则代表整个表达式
例如:[ ]+(\w+)[ ]+\1 等价于 [ ]+(\w+)[ ]+(\w+) 加上前后的 (\w+) 必须一致的约束
值得注意的是目前 Go 内置的正则引擎暂时不支持此特性,JS 是支持这种用法的
使用回溯引用进行替换
一个搜索模式和一个替换模式总共两个模式串可以完成较为复杂的替换功能
在JS下可以在替换模式中使用 $N 的方式可以引用第 N 个子表达式匹配到的内容从而拼接出我们期望的新串
这种特性经常用于字符串的重排版
使用元字符来进行新串的大小写转换
使用 \U 或 \L 作为起始,以 \E 作为结束,中间的串都会被转换成大写或小写
使用 \u 和 \l 只能把下一个字符(或子表达式)转化为大小写
前后查找
从匹配的模式串中提取我们关心的那一部分结果需要使用前后匹配 常见的实现都支持向前查找,JS 不支持向后查找,其他大多数语言支持向后查找
任何一个子表达式都可以转换为一个向前查找表达式,只要加上 ?= 前缀即可,表示只要这个表达式之前的结果
同理 ?<= 表示只要这个表达式之后的结果(向后查找)
例如:只提取金额后面的数字 (?<=\$)[0-9.]+
嵌入条件
并非所有的实现都支持条件处理
回溯引用条件
只在一个前面的子表达式搜索成功时才允许使用这个表达式
语法:(?(N)子表达式)
表示当第N个表达式搜索成功时,才会执行这个括号内的表达式
搭配使用方式:((?(N)表达式1)|表达式2)
可以达到语义上的
if N:
表达式1
else:
表达式2
类似条件语句的效果
前后查找条件
只需要把回溯引用编号改为向前查找或者向后查找表达式即可
可以表达要么都出现要么都不出现的条件语义(使用可选表达式也能达到目的)
例如:``(?(?=-)-\d{4})``` 向前查找 - ,如果成功,再多匹配四位数字
阅读