正则表达式
正则表达式的用途
请考虑以下几个场景
- 如何解析一条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