正则表达式一般整理
2018-12-19 00:50:59 27 举报
AI智能生成
正则表达式一般整理
作者其他创作
大纲/内容
表达式
正则表达式是一系列表达式的组合
普通字符也是一个表达式
a 是一个正则表达式,匹配字符 a
abc 是一个正则表达式,由三个更小的表达式组合而成 a b c
特殊表达式
.
换行符以外的所有字符
\w
字母或数字或下划线
等价于 [a-zA-Z0-9_]
\d
数字
等价于 [0-9]
\s
所有空白字符
\b
单词开始或结束的位置(单词边界),不匹配字符
例如
\ba\w+ 匹配 a开头的单词
^
字符串开始的位置
$
字符串结束的位置
\W, \D, \S, \B
换大写字母表示取反
例如
\D 表示非数字,\B 表示除了单词边界以外的位置
重复
一般量词
*
零次或多次 >= 0
?
零次或一次 == 1 || == 0
+
一次或多次 >= 1
{n}
n次
{n,}
大于等于n次
* 和 + 等价于 {0,} 和 {1,}
{n,m}
大于等于n次,小于等于m次
? 等价于 {0,1}
贪婪与非贪婪
一般量词均为贪婪模式,在满足表达式可以匹配到的情况下,匹配尽量多的字符
例如 a.*b 匹配 "aabab" 默认情况下会匹配整个字符串
- a 匹配开头的字符 a
- .* 匹配中间的 "aba"
- b 匹配结尾的 b。此为贪婪模式
如果在普通量词后面加上问号(?),则会将普通量词转换为非贪婪模式
在满足表达式可以匹配到的情况下,匹配尽量少的字符
在满足表达式可以匹配到的情况下,匹配尽量少的字符
例如 a.*?b 匹配 "aabab" 会匹配到 "aab"
- a 匹配开头的字符 a
- .* 原本表示所有字符重复任意次,会匹配尽量多的字符,添加非贪婪符号之后,会匹配尽量少的字符,.*?匹配中间的 a
- b 匹配中间的 b
非贪婪量词
(懒惰量词)
(懒惰量词)
*? +? ?? {n,m}? {n,}? 均为一般量词对应的非贪婪版本,在表达式可以匹配到的情况下,匹配尽量少的字符
{n}? 无效, 因为 {n} 表示确定的数量,不存在贪婪或非贪婪
范围
[exp]
表示一定的字符范围(exp代表范围描述表达式)
[abc]
普通枚举范围,在中括号中填入所有可能的字符
[abc] 表示单个字符 a 或 b 或 c
[\dabc]
可以结合其他描述符号
[\dabc] 表示单个字符 a, b, c 或数字 0 到 9 , 等价于 [abc\d]
[a-f1-5]
连续范围,适用于字母和数字,可以指定特定的字符范围
当 - 在开头或结尾时表示 - 符号本身(短横线)
当 - 在开头或结尾时表示 - 符号本身(短横线)
[a-f1-5] 表示 a 到 f 或 1 到 5 之内的单个字符
[5-1] 无法匹配,属于非法表达式
[-15] 或 [15-] 表示单个字符 5 或 1 或 -
[^exp]
以 ^ 开头表示取反
^ 不在开头位置时表示 ^ 符号本身
^ 不在开头位置时表示 ^ 符号本身
[^abc] 表示除了 abc 之外的单个字符
[^a-f123] 表示除了 a 到 f 和 1,2,3 之外的单个字符
[123^] 表示单个字符 1 或 2 或 3 或 ^
注意
[] 范围表达式描述的仅仅是一个字符
或表达式 | 在范围表达式中不表示逻辑或,仅仅表示 | 符号本身
特殊符号需要转义
或表达式 | 在范围表达式中不表示逻辑或,仅仅表示 | 符号本身
特殊符号需要转义
[(123)|a] 表示单个字符 1,2,3,|或a 不表示 "123" 或 a
(123)|a 表示字符串 "123" 或 单个字符 a
(123)|a 表示字符串 "123" 或 单个字符 a
[\(123\)a] 表示单个字符 (,1,2,3,)或a
[\[\]] 表示单个字符 [ 或 ]
分组
(捕获组)
(捕获组)
() 小括号表示分组
表示把一组表达式组合成一个整体,可以改变表达式顺序
一般和 重复 组合使用,分组之后即可对此分组整体进行描述
表示把一组表达式组合成一个整体,可以改变表达式顺序
一般和 重复 组合使用,分组之后即可对此分组整体进行描述
(abc)*d 表示 abc 重复若干次之后跟着一个字符 d
abc|d 表示 "abc" 或 d
ab(c|d) 表示 "abc" 或 "abd"
命名
分组会被命名
(exp)
自动命名
自动命名
每个分组都有编号,编号从0开始,编号0的分组表示整个匹配的表达式
自左向右,分组的左括号 "(" 出现的顺序即为该分组的编号
使用 a(b(c(d)e))f 匹配字符串 "abcdefg"
- 分组0: "abcdef" 整个匹配到的字符串
- 分组1: "bcde"
- 分组2: "cde"
- 分组3: "d"
对于支持正则表达式的编程语言,一般会有用于获取分组的api
例如Java中的groups()
(?<name>exp)
命名捕获组
命名捕获组
匹配exp,并捕获文本到名称为name的分组中
(<number>\d+)[a-zA-Z]+ 匹配由数字和字母组成的字符串,并将前半部分的数组捕获,存放在名称为number的分组中
(?:exp)
匹配exp,但是匹配的文本不被存储在任何分组中,仅仅表示exp是一个整体
由于捕获组需要存储,对部分正则表达式引擎来说,不存储分组匹配的字符串可以获取性能提升
由于捕获组需要存储,对部分正则表达式引擎来说,不存储分组匹配的字符串可以获取性能提升
a(?:bc)+ 表示 a开头并且"bc"至少重复一次的字符串, "abc", "abcbc"
分组0表示整个匹配的字符串,分组1不存在
分组0表示整个匹配的字符串,分组1不存在
部分正则表达式引擎不支持此特性
(?#comment)
注释分组,会被正则表达式引擎忽略,仅仅提供用于阅读的注释
向后引用
被捕获的分组可以在后续的表达式中被引用
\数字
按照默认的分组编号来引用
(\d).*\1 匹配开始和结束都是数字并且相同的字符串,"1a1", "3asdf43"
abc(\d+)\1 匹配以abc开始,跟着两个由数字组成的相同子串的字符串,"abc11", "abc1212", "abc13241324"
\name
按照捕获组的名称来引用
零宽断言*
注意:此特性并非所有正则表达式引擎都支持
实际日常使用基本不会需要,后续再补充
平衡组*
实际日常使用基本不会需要,后续再补充
注意
不推荐使用太多的分组,对于一些正则引擎来说,太多的分组在一定情况下会导致匹配效率降低
分组太多时应当修改匹配思路,或者使用多次匹配来处理
替换
匹配到的字符串可以被替换
$分组编号
与自动命名的分组编号一致,可以在替换字符串时引用捕获到的分组
一般使用在支持正则表达式搜索/替换的文本编辑器中
创建正则表达式的一般思路
正则表达式的目的是用来描述一类有共同特点的字符串,最重要的思想是提取字符串中最一般的特点
首先寻找锚点,锚点即需要匹配的字符串中不变的部分
简单一点的为固定的字符组合
例如 "ABSas1", "ABSsdf4" 中 "ABS" 固定不变:ABS
复杂一点的需要使用描述表达式
例如 "123asdfhk", "456asdfkjje" 中 "123", "456" 都是数字:\d{3}
然后逐个描述变化的部分,提取这些变化的部分共有的特性
最主要的是范围和数量
最主要的是范围和数量
例如 "123sdf23123", "7684356sjdfg768", "392asd-q-324a-392"
固定的部分是首尾都是数字并且首位相同,变化的部分是中间
开头: \d+
结尾: \d+ 由于首尾相同,而且都不是简单的固定字符,因此需要使用捕获组和反向引用
中间: .*
因此整体的表达式为 (\d+).*\1
固定的部分是首尾都是数字并且首位相同,变化的部分是中间
开头: \d+
结尾: \d+ 由于首尾相同,而且都不是简单的固定字符,因此需要使用捕获组和反向引用
中间: .*
因此整体的表达式为 (\d+).*\1
为了提升效率和减少错误率,可以尽量描述最准确的字符范围
例如上例
首尾相同的数字都是三位,因此可以使用 \d{3} 替换 \d+
中间,可能出现的字符范围具体是小写字母、数字和短横,并且字符数量在5到11之间因此可以使用 [a-z0-9-]{5,11}
最终,更准确的表达式为 (\d{3})[a-z0-9-]{5,11}\1
首尾相同的数字都是三位,因此可以使用 \d{3} 替换 \d+
中间,可能出现的字符范围具体是小写字母、数字和短横,并且字符数量在5到11之间因此可以使用 [a-z0-9-]{5,11}
最终,更准确的表达式为 (\d{3})[a-z0-9-]{5,11}\1
对于更加复杂的情况(存在多种限制情况),可以使用逻辑或 | 来枚举每种情况
一个很有特点的例子:匹配ip地址
简单的表达式
\d{1,3)\.\d{1,3}\.\d{1,3}\.\d{1,3}
分成四段,每段以点号分隔(需要转义 \.),每段最多三个数字
会错误的匹配999.999.999.999,而这并不是一个合法的ip地址
更准确的表达式
分成四段,每段以点号分隔
每段的数字范围是 0 到 255
由于正则中不存在算数运算,需要将数字当做字符来看待
因此需要枚举可能的情况
由于正则中不存在算数运算,需要将数字当做字符来看待
因此需要枚举可能的情况
一位数字 0 到 9
[0-9]
两位数字 10 到 99
第一位为 1 或 9
[19]
第二位为 0 到 9
[0-9]
[19][0-9]
同 (1[0-9])|(9[0-9])
但是明显 [19][0-9] 写法更简洁高效
但是明显 [19][0-9] 写法更简洁高效
三位数字 100 到 255
当第一位是 1 时 剩下的两位是 00 到 99
1[0-9]{2}
当第一位是 2 时 剩下的两位是 00 到 55
注意,并不是 2[0-5]{2}
因为 00 到 55 包含了 19, 28, 36 等
需要再细分
因为 00 到 55 包含了 19, 28, 36 等
需要再细分
当第二位是 0 到 4 时,第三位是 0 到 9
2[0-4][0-9]
当第二位是 5 时,第三位是 0 到 5
25[0-5]
因此 2[0-4][0-9]|25[0-5]
或者 2([0-4][0-9]|5[0-5])
因此 1[0-9]{2}|2([0-4][0-9]|5[0-5])
因此,描述一个段的表达式是 [0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5])
更清晰的表示
[0-9]
一位
一位
|
或
或
[19][0-9]
两位
两位
|
或
或
1[0-9]{2}
三位, 首位1
三位, 首位1
|
或
或
2([0-4][0-9]|5[0-5])
三位,首位2
三位,首位2
因此准确描述一个ip地址(十进制)的表达式是
([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))\.([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))\.([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))\.([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))
可以简化为
(([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))\.){3}([0-9]|[19][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))
最后,准确是相对的,不存在绝对准确的正则表达式(除非样本允许),满足需求即可,太过复杂的表达式效率会降低
参考
RegexBuddy 可视化工具
http://www.regexbuddy.com/
http://www.regexbuddy.com/
RegexBuddy 工具教程
https://www.jianshu.com/p/65f9ccb01b34
https://www.jianshu.com/p/65f9ccb01b34
菜鸟教程 正则表达式
http://www.runoob.com/regexp/regexp-syntax.html
http://www.runoob.com/regexp/regexp-syntax.html
【详细】正则表达式30分钟入门教程
https://www.cnblogs.com/sunny3096/p/7201403.html
https://www.cnblogs.com/sunny3096/p/7201403.html
0 条评论
下一页