3

如何构建一个正则表达式来匹配所有具有 ABC、DBE、ABE、FBG 等但不匹配 XBZ 的序列?

我的示例序列 ABC、DBE 等仅具有代表性。我不是在寻找那些特定的模式。A、B、C、D、E等可以采用任何模式的形式。例如,X、B 和 Z 可以是单词。

具体来说,我希望找到所有包含 B 但前面没有 X 或后面没有 Z 的实例。

我想出了一个使用grep -v反转匹配的选项的解决方案:

cat file | grep -ne ".*B.*" | grep -ve "XBZ"

但我宁愿有一个正则表达式。

4

7 回答 7

3

到达那里需要一段时间,但这种模式:

(.*((?!X).B|B(?!Z).))|(^B)|(B$)

寻找(不是 X 的东西)B 或 B(不是 Z 的东西)。TDD代码如下:

[Test]
public void TestPattern()
{
    const string pattern = "(.*((?!X).B|B(?!Z).))|(^B)|(B$)";

    Assert.IsFalse(Regex.IsMatch("Hello", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello ABC", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello DBE", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello ABE", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello FBG", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello ABC World", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello DBE World", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello ABE World", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello FBG World", pattern));
    Assert.IsTrue(Regex.IsMatch("ABC World", pattern));
    Assert.IsTrue(Regex.IsMatch("DBE World", pattern));
    Assert.IsTrue(Regex.IsMatch("ABE World", pattern));
    Assert.IsTrue(Regex.IsMatch("FBG World", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello DBE World XBZ", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello ABE World XBZ", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello FBG World XBZ", pattern));
    Assert.IsFalse(Regex.IsMatch("Hello XBZ", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello XB", pattern));
    Assert.IsTrue(Regex.IsMatch("Hello BZ", pattern));
    Assert.IsTrue(Regex.IsMatch("XB Hello", pattern));
    Assert.IsTrue(Regex.IsMatch("BZ Hello", pattern));
    Assert.IsTrue(Regex.IsMatch("B", pattern));
}
于 2013-06-28T11:29:23.627 回答
2

虽然正则表达式在否定下是封闭的,但标准正则表达式中没有否定运算符。这纯粹是语法问题,没有什么能阻止正则表达式引擎编写者在语法中添加非标准的否定运算符......所以,它必须被重写为一组替代方案:

^([^X]..|X[^B].|XB[^Z])$

我不知道更好的方法...

^PS里面有否定运算符[...],但它只匹配单个字符。它在上面使用。

于 2013-06-27T16:36:16.143 回答
2

这是完成这项工作的 perl 方法:

my $re = qr/(?<!X)B(?!Z)/;
while(<DATA>) {
    chomp;
    say /$re/ ? "OK : $_" : "KO : $_";
}
__DATA__
ABC
DBE
ABE
FBG
XBZ

输出:

OK : ABC
OK : DBE
OK : ABE
OK : FBG
KO : XBZ

解释:

(?-imsx:(?<!X)B(?!Z))

matches as follows:

NODE                     EXPLANATION
----------------------------------------------------------------------
(?-imsx:                 group, but do not capture (case-sensitive)
                         (with ^ and $ matching normally) (with . not
                         matching \n) (matching whitespace and #
                         normally):
----------------------------------------------------------------------
  (?<!                     look behind to see if there is not:
----------------------------------------------------------------------
    X                        'X'
----------------------------------------------------------------------
  )                        end of look-behind
----------------------------------------------------------------------
  B                        'B'
----------------------------------------------------------------------
  (?!                      look ahead to see if there is not:
----------------------------------------------------------------------
    Z                        'Z'
----------------------------------------------------------------------
  )                        end of look-ahead
----------------------------------------------------------------------
)                        end of grouping
----------------------------------------------------------------------
于 2013-06-28T12:32:21.313 回答
1

您可以使用否定的前瞻性断言来做到这一点

(?!^XBZ$)
于 2013-06-27T16:33:39.463 回答
1

我编写了一个函数来根据我评论中的假设编写一个正则表达式。以下是假设:

  • 这是三个字符串
  • 第一个字符取自字母表
  • 字符二总是一样的。在 OP 的帖子中,这是 B.
  • 字符三是字符一+1。
  • 字符一和三不能等于字符二。

    static void writeRegex(char skip)
    {
    string mydocpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    StringBuilder sb = new StringBuilder();
    sb.Append("^(");
    char one = 'A';
    char two = 'B';
    bool first = true;
    for (; one < 'Z' && two <= 'Z' ; )
    {
        if (!first)
        {
            sb.Append("|");   
        }
        first = false;
    
        if (one == skip)
        {
            one++;
        }
        if (two == skip || one == two)
        {
            two++;
        }
    
        sb.Append(one.ToString() + skip.ToString() + two.ToString());
    
        one++;
        two++;
    }
    sb.Append(")$");
    
    using (StreamWriter outfile = new StreamWriter(mydocpath + @"\Regex.txt"))
    {
        outfile.Write(sb.ToString());
    }
    

    }

当给定'B'的输入时,这会产生:

^(ABC|CBD|DBE|EBF|FBG|GBH|HBI|IBJ|JBK|KBL|LBM|MBN|NBO|OBP|PBQ|QBR|RBS|SBT|TBU|UBV|VBW|WBX|XBY|YBZ) $

没有否定,只有三个字符的所有可接受的结构的蛮力。

于 2013-06-27T17:37:02.810 回答
1

W3C 用于指定XMLXQuery的表示法具有-排除运算符,它可以非常方便地使用。例如,请参阅此规则(不区分大小写)排除单词“XML”:

PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))

基于 DFA 的正则表达式引擎可以通过利用正则表达式在差异下封闭的事实轻松支持这种排除。然而,您不会发现它经常实施。

一个拥有它的解析器/词法分析器生成器是REx,使用 W3C 表示法。它会在某个时候开源,但我需要更多时间来提供一些缺失的部分,尤其是文档。

使用该符号,您的示例可能如下所示:

Letter ::= [A-Z]
Three-Letter-Code ::= (Letter Letter Letter) - 'XBZ'
于 2013-06-27T20:37:09.643 回答
1

我认为人们对这个问题想得太多了。如果我正确理解了这个问题——你希望正则表达式匹配一组特定的序列,而不是其他一些特定的序列——答案很简单,你不必告诉正则表达式什么匹配。它只匹配符合您指定模式的内容,而不匹配其他内容。ABC|DBE|ABE|FBG匹配 ABC 或 DBE 或 ABE 或 FBG,不匹配任何其他序列,包括 XBZ。您不必专门指示它不匹配 XBZ。

于 2013-06-27T20:54:20.257 回答