【问题标题】:regex: negative lookbehind in negation character class? (.NET flavour)正则表达式:否定字符类中的否定后视? (.NET 风格)
【发布时间】:2010-02-01 21:28:07
【问题描述】:

我正在尝试做的事情:删除围绕特定未转义字符的最里面的未转义方括号(\ 是转义)

输入:[\[x\]]\]\[[\[y\]]
在 y 周围查找括号时的输出:[\[x\]]\]\[\[y\]
查找 x 周围的括号时的输出:\[x\]\]\[[\[y\]]

简而言之,只删除特定字符周围未转义的括号集。

我试过这个(对于 y):Regex.Replace(input, @"(?<!\\)\[(.*?(?<!\\)y.*?)(?<!\\)\]",@"$1",但这似乎与第一个未转义的 [(在 x 之前)与最后一个 ] 匹配。我想我可以用否定字符类替换. 通配符以排除[],但我真正需要否定的是这些的未转义 版本,当我尝试在否定字符类中加入像(?<!\\) 这样的否定后视,我似乎根本不匹配。

提前感谢您的时间和精力。

编辑:

为了澄清,未转义方括号的内容可以是任何内容(另一个未转义方括号除外),只要它们包含感兴趣的未转义字符 (y)。括号内的所有内容都应该保留。

【问题讨论】:

    标签: .net regex lookaround


    【解决方案1】:

    Lookbehind 不适合这项工作。试试这个:

    Regex r = new Regex(
      @"\[((?>(?:[^y\[\]\\]|\\.)*)y(?>(?:[^\[\]\\]|\\.)*))\]");
    
    string s1 = @"[\[x\]]\]\[[\[y\]]";
    Console.WriteLine(s1);
    Console.WriteLine(r.Replace(s1, @"%$1%"));
    
    Console.WriteLine();
    
    string s2 = @"[\[x\]]\]\[[1234(\[abcycba\]\y\y)]";
    Console.WriteLine(s2);
    Console.WriteLine(r.Replace(s2, @"%$1%"));
    

    结果:

    [\[x\]]\]\[[\[y\]]
    [\[x\]]\]\[%\[y\]%

    [\[x\]]\]\[[1234(\[abcycba\]\y\y)]
    [\[x\]]\]\[%1234(\[abcycba\]\y\y)%

    (我用% 替换了括号,而不是删除它们,以便更容易看到被替换的确切内容。)

    (?:\\.|[^y\[\]\\])* 匹配零个或多个 (1) 反斜杠后跟任何字符,或 (2) 任何不是“y”、方括号或反斜杠的内容。如果下一个字符是 'y',它将被消耗,(?:\\.|[^\[\]\\])* 匹配任何剩余的字符,直到下一个未转义的括号。在否定字符类中包含 both 括号(以及反斜杠)可确保您只匹配最里面的一组未转义的括号。

    使用atomic groups--即(?>...)也很重要;这可以防止回溯,我们知道回溯是无用的,当正则表达式用于不包含匹配项的字符串时,这可能会导致严重的性能问题。

    另一种方法是使用前瞻来断言“y”的存在,然后使用更简单的(?>(?:\\.|[^\[\]\\])*) 来使用括号之间的字符。问题是您现在要对字符串进行两次传递,并且要确保前瞻不会向前看太远,或者不够远,这可能会很棘手。一次性完成所有工作可以更轻松地跟踪您在匹配过程的每个阶段的位置。

    【讨论】:

    • 哇。 +1 表示努力、彻底、+1-ing 非正则表达式解决方案,并促使我最终了解原子组(可以说原子分组正则表达式相当于 || 运算符吗?)。这看起来不错;我明天将通过我的单元测试运行它并相应地接受。谢谢。
    • 如果我要将 || 与任何东西进行比较,那将是交替:在 .NET 等 Perl 兼容的正则表达式风格中,交替就像 || 一样短路,而在 DFA 或 POSIX 风格中, 总是检查所有备选方案。原子团让我想起了《吃豆人》中的能量弹。 :P
    【解决方案2】:

    为此编写正则表达式可能会导致问题过于复杂。虽然这个函数有点冗长,但它在概念上很简单并且可以解决问题:

        string FixString(char x, string original)
        {
            int i = 0;
            string s = original;
            while (i < s.Length)
            {
                if (s[i] == x)
                {
                    bool found = false;
                    for (int j = i + 1; (j < s.Length) && !found; j++)
                    {
                        if ((s[j] == ']') &&
                            (s[j-1] != '\\'))
                        {
                            s = s.Remove(j, 1);
                            found = true;
                        }
                    }
                    if (i > 0)
                    {
                        found = false;
                        for (int j = i - 1; (j >= 0) && !found; j--)
                        {
                            if ((s[j] == '[') &&
                                ( (j == 0) ||
                                  (s[j - 1] != '\\') ))
                            {
                                s = s.Remove(j, 1);
                                i--;
                                found = true;
                            }
                        }
                    }
                }
                i++;
            }
    
            return s;
        }
    

    【讨论】:

    • +1 用于透视检查。正则表达式可能是也可能不是必需的技能(正如一些人所争论的那样),但是每个程序员必须能够像您一样解决这样的“速成”问题。但我会使用 StringBuilder,并避免使用 Remove 或等效项。 :-)
    • +1 因为我通常会告诉人们对正则表达式的疯狂应用感到冷静。谢谢。
    【解决方案3】:

    问题编辑后编辑

    Regex.Replace(input, @"((?<!\\)\[(?=((\\\[)|[^[])*((?<!\\)y)))|((?<=[^\\]y((\\\]|[^]]))*)(?<!\\)\])","");
    

    我们要匹配要删除的括号:

    (?<!\\)\[ - Match is an unescaped left bracket
    (?=((\\\[)|[^[])*((?<!\\)y)) - Match is followed by any number of (escaped left brackets or non-left brackets) followed by an unescaped y
    
    | - OR
    
    (?<=[^\\]y((\\\]|[^]]))*) - Match is preceded by unescaped y followed by any number of (escaped right brackets or non-right brackets)
    (?<!\\)\] - Match is an unescaped right bracket
    

    【讨论】:

    • 感谢您的关注!我现在有. 的原因是因为未转义括号的内容可以是任何东西(当然除了另一个未转义的括号),只要它在某处有未转义的xy。因此,如果输入为 [\[x\]]\]\[[1234(\[abcycba\]\y\y)],则输出应为 [\[x\]]\]\[1234(\[abcycba\]\y\y)包含未转义 y 的未转义括号被删除。我将编辑问题以澄清。
    • 可能还有一些多余的括号可以去掉。
    猜你喜欢
    • 2011-12-07
    • 1970-01-01
    • 2014-04-19
    • 2014-11-21
    • 2014-01-15
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多