正则表达式: 使用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)的表达式 这里的反向指的是在右边表达式的匹配项的位置上,从右到左查找 零宽度断言十分有用,例如当我们需要依据边界来获取内容,但又不希望匹配到边界字符本身的时候,使用预查十分有效

其它常用元字符

  • 在实现上,此前所说的*+?等由于拥有额外的含义,因此自然地需要区分转义与非转义,这些字符称为元字符 元字符不一定是转义/非转义字符,例如*+?等不需要用\转义就能表达特殊含义,bz等需要用\转义才能表达特殊含义
  • .:匹配除\n外的任意字符
  • 锚点字符:确定匹配位置,它们是零宽度的断言,仅匹配但不消耗字符,可以理解为匹配一个间隙 实际上它们算是一些语法糖,都可以用预查来表示 ^:匹配一行的开始位置 $:匹配一行的结束位置 \b:匹配字母与非字母之间的位置 \B:匹配字母与字母之间的位置 \G:匹配上一次匹配结束的位置,用于链式搜索 \A\Z\z:匹配整个文件的开始、第一行结束、结束
  • 量词字符:作用于前面的正则表达式,表示匹配的个数
    • *:匹配任意次
    • ?:匹配至多一次
    • +:匹配至少一次
    • {n}:匹配恰好n
    • {n, }:匹配至少n
    • {a, b}:匹配至少a次、至多b
    • 惰性匹配:在上述量词后添加?,这个匹配项会尽可能少地匹配字符
  • 字符组:匹配集合内的单个字符
    • []:字符集合应由[]括住
    • -:在字符集合里,如果被两个字符夹住,则a-b表示从ab的连续字符 例如[a-z]表示匹配一个az的字符
    • ^:若出现在[]内的开始位置,则表示反集,匹配不在集合内的单个字符 例如[^a-z]表示匹配一个不是az的字符
    • 此外,还有一些元字符等价于常用的字符组(但可能部分语言不支持):
      • \w等价于[a-zA-Z0-9]\W等价于[^a-zA-Z0-9]
      • \s等价于[ \t\n\r]\S等价于[^ \t\n\r]
      • \d等价于[0-9]\D等价于[^0-9]
  • |:用于分割不同的表达式,通常用法是由()包裹并随之使用量词来实现分支结合多次选择的效果 |的优先级是小于()
  • 关于转义:需要看被什么环境包裹
    • 如果最外层为(),说明内部会被视为正常的完整的表达式,所有元字符是否转义的规则同上
    • 如果除去最外层的所有()后剩下的最外层为[],说明内部是字符类,大部分字符(包括元字符)不需要用\转义就能表示普通字符

VimRegex

SQLRegex