浅析正则表达式

312 阅读3分钟

正则表达式

正则表达式的用途

请考虑以下几个场景

  • 如何解析一条SQL语句的查询表名
  • 如何解析一条SQL语句的查询字段
  • 如何修改一条SQL语句

我们可以用Druid, ANTLR, Calcite等开源库解决方案。

另一种解决方案是使用正则表达式。可以用一些精心构建的语句,或者说一些文本和特殊指令构成的高度简练的字符串来解决,比如像下面这样的语句:

(?<=\bselect\b)[\s\S]*?(?=\bfrom\b)

匹配单个字符

匹配普通字符

from 是一个正则表达式

有一个sql
select f1, f2 from t

正则表达式
from

结果
from

匹配任意字符

.字符(英文句号)可以匹配任意单个字符

.字符可以匹配任意单个字符,字母,数字甚至是 .字符本身

有一个sql
select t.f1, t.f2 from t

正则表达式
t.f.
 
结果,共找到 2 处匹配:
t.f1
t.f2

匹配特殊字符

.字符在正则表达式里有着特殊的含义。

\是一个元字符,表示这个字符有着特殊含义,代表的不是字符本身。

因此,.表示匹配任意单个字符,.表示匹配.字符本身

匹配一组字符

匹配多个字符中的某一个

元字符 [ 和 ] 用来定义一个字符集合

有一个sql
select t.f1, t.f2 from t

正则表达式
[Ff][Rr][Oo][Mm]

共找到 1 处匹配:
from

利用字符集合区间

-(连字符)定义一个字符区间

有一个sql
select t.f1, t.f2 from t

正则表达式
t.f[0-9]

共找到 2 处匹配:
t.f1
t.f2

排除

元字符^对一个字符集合取非匹配

使用元字符

再谈转义

因为元字符在正则表达式里有着特殊的含义,所以这些字符就无法用来代表他们本身。

对元字符进行转义需要用到\字符。

这意味着\字符也是一个元字符,她的特殊含义是对其他元字符进行转义。

匹配空白字符

  • [\b] 回退(并删除)一个字符
  • \f 换页符
  • \n 换行符
  • \r 回车符
  • \t 制表符(Tab键)
  • \v 垂直制表符

匹配特定的字符序列

  • \d 任何一个数字字符(等价于[0-9])
  • \D 任何一个非数字字符(等价于[^0-9])
  • \w 任何一个字母数字字符或下划线字符(等价于[a-zA-Z0-9_])
  • \W 任何一个非字母数字字符或非下划线字符(等价于[^a-zA-Z0-9_])
  • \s 任何一个空白字符(等价于[\f\n\r\t\v])
  • \S 任何一个非空白字符(等价于[^\f\n\r\t\v])

重复匹配

  • * 匹配前一个字符(子表达式)的零次或多次重复
  • *? *的懒惰性版本
  • + 匹配前一个字符(子表达式)的一次或多次重复
  • +? +的懒惰性版本
  • ? 匹配前一个字符(子表达式)的零次或一次重复
  • {n} 匹配前一个字符(子表达式)的n次重复
  • {m,n} 匹配前一个字符(子表达式)的至少m次且至多n次重复
  • {n,} 匹配前一个字符(子表达式)的n次或更多次重复
  • {n,}? {n,}的懒惰性版本

防止过度匹配

搜索表名

有一个sql
select f1, f2 from t where value > 0 group by date order by time limit 100

正则表达式
(?<=\bfrom\b)[\s\S]*(?=\b(where|group by|order by|limit)\b)

共找到 1 处匹配:
 t where value > 0 group by date order by time 

为什么会这样?

因为 * 和 + 都是所谓的“贪婪型”元字符,其匹配行为是多多益善而不是适可而止。

把[\s\S]* 改为 [\s\S]*? 就好了。

位置匹配

单词边界

\b用来匹配一个单词的开头或结尾

字符串边界

^代表字符串开头,$代表字符串结尾

多行模式

(?m)启用多行模式,迫使正则表达式引擎将换行符视为字符串分隔符

使用子表达式

子表达式是更长的表达式的一部分。

划分子表达式的目的是为了将其视为单一的实体来使用。

子表达式必须出现在字符(和)之间

环视

向前查看

向前查看指定了一个必须匹配但不用在结果中返回的模式。

向前查看模式的语法是一个已?=开头的子表达式,需要匹配的文本跟在=的后面

有一个sql
select f1, f2 from t where value > 0 group by date order by time limit 100

正则表达式
[\s\S]*(?=\bfrom\b)

结果
select f1, f2 

向后查看

向后查看操作符是?<=

有一个sql
select f1, f2 from t where value > 0 group by date order by time limit 100

正则表达式
(?<=\bselect\b)[\s\S]*

结果
 f1, f2 from  t  where  value > 0 group by date order by time limit 100 

结合向前查看和向后查看

查找所有的查询字段

有一个sql
select f1, f2 from t where value > 0 group by date order by time limit 100

正则表达式
(?<=\bselect\b)[\s\S]*(?=\bfrom\b)

结果
 f1, f2 

否定式环视

  • (?=) 肯定式向前查看
  • (?!) 否定式向前查看
  • (?<=) 肯定式向后查看
  • (?<!) 否定式向后查看

附录

如何使用正则表达式

正则表达式的使用方法和具体功能在不同的应用程序/语言中各有不同。

Java语言中的正则表达式匹配功能是通过 java.util.regex.Matcher类和以下这些方法实现的。

  • find 在一个字符串里寻找一个给定模式的匹配
  • matches 用一个给定的模式去尝试匹配一个完整的字符串
  • lookingAt 用一个给定的模式去尝试匹配一个字符串的开头
  • replaceAll 执行替换操作,替换所有的匹配
  • replaceFirst 执行替换操作,只替换第一个匹配

此外, java.util.regex.Pattern类也提供了一些简单易用的包装器

  • compile 把一个正则表达式编译成模式
  • flags 返回模式的匹配标志
  • pattern 把一个模式还原为正则表达式
  • split 把一个字符串拆分为子串

Sun公司的正则表达式基于的是Perl正则表达式实现,但要注意以下几点

  • 不支持嵌入条件
  • 不支持使用\E, \l,\L,\u和\U进行大小写转换
  • 不支持使用\b匹配退格符
  • 不支持\z