这两天做sqlserver数据库迁移到Oracle的工作,由于top在oracle中不能用,为了省事,选择用正则去替换。
正则这个东西,不用就忘,好忧桑,搞了了n久最后整出了这么个玩意
1
([^\<\/]select\s*)top\s*(\$\{\w{4}\})([\s\S]*?from[\s\S]*?where)
$1$3 ROWNUM \<\= $2 AND
2
([^\<\/]select\s*)top\s*(\d+?\b)([\s\S]*?from[\s\S]*?where)
$1$3 ROWNUM \<\= $2 AND
3
([^\<\/]select\s*)top\s*(\(\#\{.*?\}\))([\s\S]*?from[\s\S]*?where)
$1$3 ROWNUM \<\= $2 AND
组合
([^\<\/]select\s*)top\s*(\$\{\w{4}\}|\d+?\b|\(\#\{.*?\}\))(?![\s\S]*?select)[\s\S]*?from(?![\s\S]*?select)[\s\S]*?where
([^\<\/]select\s*)top\s*(\$\{\w{4}\}|\d+?\b|\(\#\{.*?\}\))[\s\S]*?(?=where)
最终
([^\<\/]select\s*)top\s*(\$\{\w{4}\}|\d+?\b|\(\#\{.*?\}\))((?!select)[\s\S])*?from((?!select)[\s\S])*?where
基础语法就不说了,复制粘贴一下,不熟的写一下:
\b 这个是匹配单词边界,感觉比较实用
. 英文的点,匹配除换行符以外的任意字符,这个除换行符需要注意一下,一般在大文本匹配中都有换行的,用.*?这种就不灵了
匹配换行符:一般用这种方式 [\s\S] ,这样就可以匹配到所有的字符包括换行。
* 重复零次或多次 + 重复一次或多次,这个次数需要注意一下
? 这个比较重要 加问号就是非贪婪的(最小需求),否则就最大匹配了
分组:
这个比较实用,也可用于调试,一句换概括,就是用()保卫起来就是一组
在替换中可以用$0$1$2。。。的方式吧组的值取出来
注意一点:$0指的是整个字符串的匹配
关于匹配的顺序:
正则匹配夹贪婪后并不是双向的最小匹配,只是从做到右是最小的
例如:a.*?b 匹配aabcb 结果是aab而不是ab
正则的匹配顺序是从左至右的
如何双向最小匹配呢?? 我就是在这个上面饶了好久好久,其实现在也不是搞的特别明白。
关于这个先要补充点关于零宽断言的知识。
下面是复制粘贴:
"(exp)" 匹配exp,并捕获文本到自动命名的组里
"(?<name>exp)" 匹配exp,并捕获文本到名称为name的组里
"(?:exp)" 匹配exp,不捕获匹配的文本,也不给此分组分配组号
上面三个不重要,关键是下面四个:
以下为零宽断言
"(?=exp)" 匹配exp前面的位置
如 "How are you doing" 正则"(?<txt>.+(?=ing))" 这里取ing前所有的字符,并定义了一个捕获分组名字为 "txt" 而"txt"这个组里的值为"How are you do";(重点理解什么是位置:)
"(?<=exp)" 匹配exp后面的位置
如 "How are you doing" 正则"(?<txt>(?<=How).+)" 这里取"How"之后所有的字符,并定义了一个捕获分组名字为 "txt" 而"txt"这个组里的值为" are you doing";
难点就是下面两个,简直变态:
"(?!exp)" 匹配后面跟的不是exp的位置
如 "123abc" 正则 "\d{3}(?!\d)"匹配3位数字后非数字的结果
"(?<!exp)" 匹配前面不是exp的位置
如 "abc123 " 正则 "(?<![0-9])123" 匹配"123"前面是非数字的结果也可写成"(?!<\d)123"
理解一下这个:"(?!exp)" 匹配后面跟的不是exp的位置
A(?!B) ,也就是说A后面紧跟的就不能是B了,否则A就不符合条件 其实直接用的话有一个问题
例如:abcfgfhadsssfh a(.*?)(?!c)f
这个匹配执行:首先匹配到a,(.*?)最小可以是0,所有先从0开始,a(.*?)匹配到a,a(.*?)(?!c)由于a的后面不是c即a(.*?)(?!c)匹配到a,接着往下走,
a的后面不是f,匹配不成功,接着(.*?)取1,即a(.*?)匹配到ab,由于ab后面是c不符合,(.*?)取2即abc,abc后面不是c,a(.*?)(?!c)
匹配到abc,最后一步a(.*?)(?!c)f匹配到abcf。
跟预计的af之间不含c的结果不太一样吧。。。还是上面的字符串,如何实现a开头f结尾,中间不含c呢? a((?!c).)+?f
在这我已经傻傻分不清楚了:头疼,明天接着写!
第二天醒来洗个澡果然神清气爽,接着来拔:
换个思路,首先搞明白表达式a((?!c).)+?f中((?!c).)表示的含义,这部分表示匹配不包含c字符位置后的一个字符,所以,((?!c).)+?即表示不包含c字符位置后的一个字符有一个或者多个(贪心)。再回到匹配过程,首先匹配到a,然后(?!c)匹配到ab之间的位置,即((?!c).)匹配到b(((?!c).)后面是+,从1开始),此时a((?!c).)+?匹配到了ab,往下走,字符串中ab后面是c,不符合要求。接着+号起作用,匹配2个((?!c).),第一个((?!c).)匹配的是b,从b开始匹配bc之间的位置不符合要求,直接结束第一个a的匹配,找寻下一个a,依次类推。
难点就是这玩意((?!c).),这个就是表示匹配后面不是c的位置的一个字符,可以假设这个是一个字符设为b,那么ab+?f,匹配到的只能是abbbbb。。f这种形式,af中间肯定都是b(一个或多个),所以a((?!c).)+?f匹配到的肯定是af中间包括一个或多个不包含c字符的位置后的一个字符。这样是不是清楚一点点。。
差不多就这么多了。
还有一点要注意的是空格,匹配时你不会清楚文本中他人写了几个空格的,想要全匹配,最好用\s*或者\s+这种方式。
长路漫漫啊!