正则表达式: 使用regex进行模糊查询
Regex
基本介绍
Regex全称Regular Expression,译为正则表达式或正规表达式Regex最常用于文本内容的模糊搜索,旨在使用一系列的语法规则高效地匹配任意文本- 抽象定义如下:
- 字母表:由所有可能出现的字符组成的集合
- 串:空串以及字符集中的字符的组合
- 语言:串的集合
- 正则表达式:经过形式化描述的规则,文本内容(整个语言)经过给定的正则表达式得到匹配结果(语言的子集,称为正规集)
- 正则表达式用一系列简洁的符号来表示语言(字符串集合)的运算:
- 语言的并(即两边集合的任意元素):
(r) | (s) - 语言的连接(即在左边集合的任意串后拼接右边集合的任意串):
(r)(s) - 语言的闭包(即集合中的串出现任意次地连接起来):
(r)* - 语言的正闭包(即集合中的串出现至少一次地连接起来):
(r)+实际上是一个语法糖,等价于(r)(r)* - 语言与空串的并(语法糖),表示集合中的串出现至多一次:
(r)? - 运算的优先级:
* > 连接 > |
- 语言的并(即两边集合的任意元素):
- 在实现上,正则表达式拥有多套标准
POSIX在制定类Unix操作系统的接口时顺带制定了regex的标准,POSIX标准又分为基础BRE与扩展ERE两种Perl语言经过多次迭代后由于其regex功能十分强大好用,出现了PCRE标准,可以说它是一套工业标准
PCRE
PCRE全称Perl Compatible Regular Expressions,是用C编写的兼容Perl(一种不再流行的语言)正规式语法的正则表达式库 现在的大多数编程语言的正则表达式库属于PCRE派系,因此它也成为了一种标准
匹配、分组、环视
- 默认情况下,正则表达式会正向扫描,并将匹配成功的文本消耗掉,因此实现单遍扫描的效果 如果希望使用已经被扫描过的字符,可以由以下方法实现:
- 默认情况下,正则表达式会贪婪地消耗字符,例如面对
a.*a这样的表达式,它不会遇到两个a就停止而是继续匹配直到遇到最后一个a - 分组:由元字符
()括起,一个分组可以表示一个完整的正则表达式,分组有以下类型:- 匿名捕获分组:类似
(exp)的分组,它会消耗匹配的字符,同时存储它们,随后可用\正整数引用第正整数个分组的匹配结果 注意引用分组结果和完整的正则表达式不一样,它们等价于匹配的结果而不是正则表达式 - 非捕获分组:类似
(?:exp)的分组,它会消耗匹配的字符,但不会存储它们 - 命名捕获分组:类似
(?P<name> exp)的分组,在匿名捕获分组的基础上,它会将匹配结果赋给name,命名分组同时也会占用整数引用(相当于有两个名字) 定义命名捕获分组后,可以使用(?P=<name>)引用由name这个分组匹配到的字符串
- 匿名捕获分组:类似
- 如果希望不消耗字符,即多遍扫描,可以使用环视,或者称为预查
预查是一种零宽度断言,会不消耗字符地检查后续(正向)、之前(反向)的文本是否存在能被
exp匹配上的匹配项 这种行为通常用于条件判断,防止匹配过程中把检查项中间的文本也消耗掉了,导致中间的文本没有经过检查 肯定正向预查:类似(?=exp)的表达式 否定正向预查:类似(?!exp)的表达式,返回肯定正向预查的相反值 肯定反向预查:类似(?<=exp)的表达式 否定反向预查:类似(?<!exp)的表达式 这里的反向指的是在右边表达式的匹配项的位置上,从右到左查找 零宽度断言十分有用,例如当我们需要依据边界来获取内容,但又不希望匹配到边界字符本身的时候,使用预查十分有效
其它常用元字符
- 在实现上,此前所说的
*、+、?等由于拥有额外的含义,因此自然地需要区分转义与非转义,这些字符称为元字符 元字符不一定是转义/非转义字符,例如*、+、?等不需要用\转义就能表达特殊含义,b、z等需要用\转义才能表达特殊含义 .:匹配除\n外的任意字符- 锚点字符:确定匹配位置,它们是零宽度的断言,仅匹配但不消耗字符,可以理解为匹配一个间隙
实际上它们算是一些语法糖,都可以用预查来表示
^:匹配一行的开始位置$:匹配一行的结束位置\b:匹配字母与非字母之间的位置\B:匹配字母与字母之间的位置\G:匹配上一次匹配结束的位置,用于链式搜索\A、\Z、\z:匹配整个文件的开始、第一行结束、结束 - 量词字符:作用于前面的正则表达式,表示匹配的个数
*:匹配任意次?:匹配至多一次+:匹配至少一次{n}:匹配恰好n次{n, }:匹配至少n次{a, b}:匹配至少a次、至多b次- 惰性匹配:在上述量词后添加
?,这个匹配项会尽可能少地匹配字符
- 字符组:匹配集合内的单个字符
[]:字符集合应由[]括住-:在字符集合里,如果被两个字符夹住,则a-b表示从a到b的连续字符 例如[a-z]表示匹配一个a到z的字符^:若出现在[]内的开始位置,则表示反集,匹配不在集合内的单个字符 例如[^a-z]表示匹配一个不是a到z的字符- 此外,还有一些元字符等价于常用的字符组(但可能部分语言不支持):
\w等价于[a-zA-Z0-9],\W等价于[^a-zA-Z0-9]\s等价于[ \t\n\r],\S等价于[^ \t\n\r]\d等价于[0-9],\D等价于[^0-9]
|:用于分割不同的表达式,通常用法是由()包裹并随之使用量词来实现分支结合多次选择的效果|的优先级是小于()的- 关于转义:需要看被什么环境包裹
- 如果最外层为
(),说明内部会被视为正常的完整的表达式,所有元字符是否转义的规则同上 - 如果除去最外层的所有
()后剩下的最外层为[],说明内部是字符类,大部分字符(包括元字符)不需要用\转义就能表示普通字符
- 如果最外层为