关于c#:正则表达式中的双重负面看法

Double negative lookbehind in regex

我正在筛选包含特定人员ID的SQL脚本中的行。为了我的目的,最好是在过滤时过于热心,而不是在过滤下面,但我还是要小心一点。

例如,如果其中一个个人ID是123,并且一行包含[blob_id] = 123,我不想过滤掉它。所以,我想找到123(两边都有单词分隔符),只要前面没有[] ==person_id

regex应该与以下每一行匹配:

1
2
3
123
[person_id] = 123
blah,blah,123,blah

它不应该与每一行匹配:

1
2
foo123bar
[blob_id] = 123

我以为这个雷杰克斯会起作用:

1
(?<!\[(?!person_id)\] = )\b123\b

后面的外部负面评论说:"字符串不能以[] =开头。"内部展望说"这个字符串可以匹配除person_id以外的任何东西。我认为双重否定的意思是"如果这个字符串前面加上[] =只能是person_id"。

不幸的是,情况似乎并非如此。它适用于除[blob_id] = 123之外的所有测试用例。

我相信,由于某种原因,由于双重否定,背后的外表与任何事物都是匹配的。

这是我的regex101与我的测试用例的链接。


因为你过滤掉了整条线,这很容易:如果你在一条线上找到了任何你不想要的东西,你可以把这条线扔掉。

如果您正在使用PCRE,则可以使用回溯动词来实现您想要的目标:

1
\bblob_id\b.+(*SKIP)(*FAIL)|\b123\b

演示

如果regex引擎在一条线上遇到blob_id,它将匹配到该线的末尾(.+),然后匹配失败,并在失败的位置重新尝试((*SKIP)(*FAIL))。这是因为发动机总是尝试从左到右的备选方案。

在c中,您没有(*SKIP)(*FAIL),因此您可以使用它:

1
\bblob_id\b.+|(?<id>\b123\b)

检查match.Groups["id"].Success。如果是假的,就把比赛扔掉。

但是在C中最好的选择是…要使用可变长度lookbehinds(C regex引擎的一个重要功能):

1
\b123\b(?<!\bblob_id\b.*)

演示

我将断言放在匹配之后只是为了优化,因此只有当引擎已经成功匹配\b123\b时,它才会实际检查lookback。

看来我误解了这个问题:

In your second demo, the only that it filters out is blob_id. I need it to filter out any id that isn't person_id.

那么,在这种情况下,你需要把那些括号放回去,告诉我什么是ID,什么是其他东西。我想我可以利用它们,因为你在你的问题上确实做到了这一点。那么双重负面的环顾是有意义的:

1
2
\b123\b(?<!\[(?!person_id\b)\w+\][^\]
]*)

演示

[^\]
]
表示除]和换行符以外的任何字符,因此您只能得到与搜索值最接近的标识符。


这可能会更清楚地解释正在发生的事情,以及如何添加确定ID的列表,同时排除您定义的括号格式。

编辑-做断言的提示。断言是自包含的构造。他们的行动独立于彼此之间。断言不知道它们是否在另一个内部断言。给他们的位置是当前位置相对于呼叫者,这可能与最外面的不同当前位置。

最后,不管断言是什么,在断言内部,表达式必须匹配..这是首要规则。该匹配的成功或失败在逻辑上解析为断言它是,即负/正结果。

埃多克斯1〔9〕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 .*                      # For testing purposes, get whole line before

 (?<!                    # Bracket ID's can't be behind the '123'
      \[                      # Open bracket [
      (?!                     # Exclude ID's that are OK to be here
           (?:
                person_id               # this id is OK
             |  ok_id                   # this id is OK (add more here, etc..)          
           )
           \]
      )
      [^\]]+                  # 1 or more id chars
      \]                      # Close bracket ]
      \s*                     # Optional whtiespace
      =                       # Equals sign
      \s*                     # Optional whtiespace
 )
 \b 123 \b

 .*                      # For testing purposes, get whole line after

代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string input = @"
123
[person_id] = 123
blah,blah,123,blah

foo123bar
[blob_id] = 123
"
;
Regex Rx123 = new Regex(@".*(?<!\[(?!(?:person_id|ok_id)\])[^\]]+\]\s*=\s*)\b123\b.*");
Match _m = Rx123.Match( input );
while (_m.Success)
{
    Console.WriteLine("Found: {0}", _m.Groups[0].Value);
    _m = _m.NextMatch();
}

输出:

1
2
3
Found: 123
Found: [person_id] = 123
Found: blah,blah,123,blah


问题是(?!person_id)不匹配任何字符。相反,您可以在负查找中使用负查找来消除实际使用负查找中任何内容的需要。

1
(?<!(?<!\[person_id)\] = )\b123\b

演示