2

所以我有以下正则表达式:

(?<!\.)\b([\w\@\-]+) *\b(IN|NOT IN|LIKE|NOT LIKE|BETWEEN|REGEXP|NOT|IS|XOR)+\b *

我希望帮助我匹配一些 SQL 代码。

但是,看起来我会对第二个括号中的短语有问题,例如“NOT IN”和“NOT LIKE”

我需要一个匹配或不匹配的正则表达式(没有像我当前的正则表达式那样的部分匹配)。

  1. customers.id NOT IN (SELECT MAX(customers_service.customer_id))根本不应该匹配
  2. customers.id NOT LIKE (SELECT MAX(customers_service.customer_id))根本不应该匹配
  3. id NOT IN (SELECT MAX(customers_service.customer_id))应该匹配
  4. id IN (SELECT MAX(customers_service.customer_id))应该匹配

我正在使用 RegexBuddy 进行检查,并使用我的正则表达式获得了第 1 和第 2 的匹配项。

还,

  1. id NOT IN (SELECT MAX(customers_service.customer_id))只匹配id NOT,而不是id NOT IN
  2. id NOT LIKE (SELECT MAX(customers_service.customer_id))只匹配id NOT,而不是id NOT LIKE

我想修改这个正则表达式来捕捉后面否定的条件,以及第二个括号中的确切短语,或者根本不匹配(不匹配)。

我怎样才能完成这项工作?

4

3 回答 3

2

首先,\b不匹配单词的开头或结尾。这就是它总是被描述的方式,但这是一个谎言。\b匹配的是一个位置,该位置后跟一个单词字符但前面(?=\w)(?<!\w)没有一个-- -- 或前面有一个单词字符但后面没有一个 -- (?<=\w)(?!\w)。如果这些条件不是您想要匹配的,那么您最好不要使用\b

您尝试匹配的名称显然可以包含标准“单词”字符(字母、数字和下划线),因此单词边界是无用的@-一般来说,为了确保匹配一个完整的单词,你会使用否定的lookbehind和否定的lookahead:

(?<![\w@-])[\w@-]+(?![\w@-])

在您的情况下,您还想确保前面的字符不是.,并且您知道下面的字符必须是空格,因此您的正则表达式的一部分将是:

(?<![.\w@-])[\w@-]+\s+

更大的问题是它也可以匹配你不希望它匹配的东西——例如,关键字NOTIN。我建议两种补救措施。首先,收紧关键字的正则表达式,使复合关键字像NOT INNOT LIKE被视为原子单元:

(?:NOT(?:\s+(?:IN|LIKE))?|IN|LIKE|BETWEEN|REGEXP|IS(?:\s+NOT)?|XOR)\b

其次,在前瞻中使用它来确保您匹配的第一个单词不是(部分)关键字。这是完整的正则表达式,为了便于阅读,分为两行:

(?<![.\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'

...这是一个演示

于 2012-12-30T06:44:05.457 回答
0

您的措辞有点令人困惑,但据我了解,负面的后视效果正如您所料。

对于“部分匹配”问题,您只需通过减少长度来对关键字进行排序:

(?<!\.)\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 解析器)。关键是为您的正则表达式提供一些区分标识符和运算符的方法;否则它无法分辨。如果你知道你所有的标识符都是小写的,那可能对你的目的有用,尽管很脆弱。

于 2012-12-28T16:27:22.920 回答
0

好吧。因此,经过多次“正则表达式”,这是对我有用的正则表达式:

(?<=\s)(?!(?:not|is)(?=\s))([\w\@\-]+)(?=\s) (?<=\s)(NOT LIKE|NOT IN|IS NOT|BETWEEN|REGEXP|LIKE|XOR|NOT|IN|IS)(?=\s)

当然,在我的preg函数中,我会使用不区分大小写的模式修饰符。

我必须从我在 StackOverflow 上发布的其他问题中找到其他部分。

干杯。

于 2012-12-29T16:46:01.877 回答