所以我有以下正则表达式:
(?<!'.)'b(['w'@'-]+) *'b(IN|NOT IN|LIKE|NOT LIKE|BETWEEN|REGEXP|NOT|IS|XOR)+'b *
我正在寻找帮助我匹配一些SQL代码。
然而,看起来我在第二个括号里的短语会有问题。'NOT IN' and 'NOT LIKE'
我需要一个匹配或不匹配的正则表达式(不像我当前的正则表达式那样部分匹配)。
-
customers.id NOT IN (SELECT MAX(customers_service.customer_id))
不匹配 -
customers.id NOT LIKE (SELECT MAX(customers_service.customer_id))
不匹配 -
id NOT IN (SELECT MAX(customers_service.customer_id))
应匹配 -
id IN (SELECT MAX(customers_service.customer_id))
应该匹配
我使用RegexBuddy来检查,我使用我的regex得到1号和2号的匹配。
,
-
id NOT IN (SELECT MAX(customers_service.customer_id))
只匹配id NOT
,而不是id NOT IN
-
id NOT LIKE (SELECT MAX(customers_service.customer_id))
只匹配id NOT
,而不是id NOT LIKE
我想修改这个正则表达式,以捕获后面的负look的条件,以及第二个括号中的确切短语,或者根本不匹配(没有部分)。
我该怎么做呢?
首先,'b
不匹配单词的开头或结尾。人们总是这么说,但这是个谎言。'b
匹配的位置是后面有一个单词字符,但前面没有一个——(?='w)(?<!'w)
——或者前面有一个单词字符,而后面没有一个——(?<='w)(?!'w)
。如果这些条件不是您想要匹配的,那么最好不要使用'b
。
你想要匹配的名字显然可以包含@
和-
以及标准的"单词"字符(字母,数字和下划线),所以单词边界是无用的。一般来说,为了确保匹配一个完整的单词,你会使用否定后看和否定前看:
(?<!['w@-])['w@-]+(?!['w@-])
在您的情况下,您还希望确保前面的字符不是.
,并且您知道后面的字符必须是空白,因此您的正则表达式的一部分将是:
(?<![.'w@-])['w@-]+'s+
更大的问题是,这也可以匹配你不希望它匹配的东西——例如:, NOT
、IN
等关键字。我建议采取两种补救措施。首先,收紧关键字的正则表达式,以便将NOT IN
和NOT LIKE
等复合关键字视为原子单元:
(?:NOT(?:'s+(?:IN|LIKE))?|IN|LIKE|BETWEEN|REGEXP|IS(?:'s+NOT)?|XOR)'b
第二,在forward中使用它来确保你匹配的第一个单词不是关键字(部分)。下面是完整的正则表达式,为了可读性分成两行:
(?<![.'w@-])(?!(?:NOT(?:'s+(?:IN|LIKE))?|IN|LIKE|BETWEEN|REGEXP|IS|XOR)'b)['w@-]+'s+
(?:NOT(?:'s+(?:IN|LIKE))?|IN|LIKE|BETWEEN|REGEXP|IS|XOR)'b's*
您可以通过为关键字定义子例程组来使其更易于维护。下面是PHP字符串文字的样子:
'~
(?(DEFINE)(?<KEYWORD>
(?:NOT(?:'s+(?:IN|LIKE))?|IN|LIKE|BETWEEN|REGEXP|IS(?:'s+NOT)?|XOR)'b
))
(?<![.'w@-])(?!(?&KEYWORD))['w@-]+'s+(?&KEYWORD)'s*
~ix'
…这里有一个演示
你的措辞有点令人困惑,但据我所知,消极的目光正如你所期望的那样起作用了。
对于"部分匹配"问题,您只需按长度递减顺序排列关键字:
(?<!'.)'b(['w'@'-]+) *'b(NOT LIKE|BETWEEN|REGEXP|NOT IN|LIKE|NOT|IN|IS|XOR)+'b *
这样,它尝试在满足于较短的关键字之前捕获"更完整"的关键字。
编辑
我现在明白是怎么回事了。对于
customers.id NOT IN (SELECT MAX(customers_service.customer_id))
存在匹配的原因是NOT
被(?<!'.)'b(['w'@'-]+)
匹配,并且IN
作为运算符被匹配。换句话说,它认为NOT
是一个列名。
解决这个问题的唯一方法是添加约束。例如,如果您知道字符串总是以表/列标识符开始,那么这样做:
^'s+(['w'@'-]+) *'b(NOT LIKE|BETWEEN|REGEXP|NOT IN|LIKE|NOT|IN|IS|XOR)+'b *
****
这样就不需要向后看,也不需要字边界了。
如果你不能做这个约束,那么它是棘手的,如果不是完全不切实际的(因为你基本上必须建立一个SQL解析器的正则表达式)。关键是给你的正则表达式一些区分标识符和操作符的方法;否则它无法判断。如果您知道您的所有标识符都是小写的,那么这可能符合您的目的,尽管这很脆弱。
那好吧。因此,经过多次"正则化",这里的正则表达式为我做到了:
(?<='s)(?!(?:not|is)(?='s))(['w'@'-]+)(?='s) (?<='s)(NOT LIKE|NOT IN|IS NOT|BETWEEN|REGEXP|LIKE|XOR|NOT|IN|IS)(?='s)
当然,在我的preg
函数中,我会使用不区分大小写的模式修饰符。
我不得不从我在StackOverflow上发布的其他问题中找到其他部分。
欢呼。