如何匹配除两个字符以外的所有内容?

时间:2012-12-12 21:42:30

标签: c# regex

我需要匹配双花括号之间的所有字符,但我需要能够在一个大字符串中找到多个匹配项。

我一直在使用这个RegEx测试人员,因为我在C#中这样做:http://derekslager.com/blog/posts/2007/09/a-better-dotnet-regular-expression-tester.ashx 另外,我检查了“SingleLine”,因为我想要。匹配\ n

以下是我匹配的字符串示例:

<div class="nest-1-2">
    <dl>
    <dt>Type:</dt>
    <dd>{{(Entity)Field Name.separator(, ) > [:Name:]}}</dd>
    <dt>At:</dt>
    <dd>{{(Entity)Field Name > [:Name:]}}</dd>
    <dt>Team:</dt>
    <dd>{{(Entity)Field Name.separator(, ) > [:First Name:] [:Last Name:]}}</dd>
    </dl>
</div>

这是我正在使用的正则表达式:

\{\{(?<field>[^>]*)?[ > ]?(?<looptemplate>[^\}\}].*)?\}\}

我遇到的问题是我希望所有内容都匹配所有文本到下一个}}这是匹配最后一个而不是下一个。所以我得到1场比赛,这是从第一个{{到最后一个}}的所有内容,我尝试使用否定前瞻(?!\}\}),但这似乎对我不起作用。不幸的是,[^\}\}]与大括号不匹配,只匹配一个。

我不是一个正则表达式的总菜鸟,但是这个人真的得到了我。我四处寻找答案,所以现在我希望有人可以帮助我。

我非常感谢专家的帮助。

4 个答案:

答案 0 :(得分:5)

一些事情:

  1. 您在包含?的捕获组上使用了**表示“0次或更多次”,因此基本上内容已经是可选的。使用?不会做任何事情。

    \{\{(?<field>[^>]*)[ > ]?(?<looptemplate>[^\}\}].*)\}\}
    
  2. [ > ]匹配 1 字符。空格或>。你可能意味着(?: > )(匹配" > "(忽略引号,否则SO不会渲染空格)并将它们组合在一起。

    \{\{(?<field>[^>]*)(?: > )?(?<looptemplate>[^\}\}].*)\}\}
    
  3. [^\}\}][^\}]相同。否定字符类不适用于字符串,它们仅适用于内部的每个字符,因此多次写入字符不会改变任何内容。我猜这就是为什么你尝试了负面的前瞻。这是对的,但你需要检查重复的每个字符的条件。否则,您只需检查一次,looptemplate不是以\}\}开头,而是您使用.*开火。所以将.和前瞻组合在一起:

    \{\{(?<field>[^>]*)(?: > )?(?<looptemplate>(?:(?!\}\}).)*)\}\}
    
  4. 您的(?: > )是可选的,因此如果您有一些{{...}}不包含它(只有field部分,您将遇到与以前相同的问题,就在这次[^>]。这里也包括前瞻:

    \{\{(?<field>(?:(?!\}})[^>])*)(?: > )?(?<looptemplate>(?:(?!\}\}).)*)\}\}
    
  5. 顺便说一下,使用否定字符类或前瞻的替代方法是使用不合理的重复。如果你可以使用否定的字符类,那通常是可取的,因为它同样可读但通常比ungreedy修饰符更有效,因为它不需要回溯。在你的情况下,你必须使用前瞻(因为有一个你不想过去的两个连续字符的模式,而不是只有一个字符)。在这种情况下,前瞻可能会通过避免回溯来抵消性能提升,而前瞻通常会略微降低可读性。所以你可能只想在这里不假思索地重复(用?附加重复量词):

    \{\{(?<field>(?:(?!\}})[^>])*)(?: > )?(?<looptemplate>.*?)\}\}
    

    请注意,您不能对field使用不合理的重复,因为(?: > )是可选的。这会导致field为空,其他所有内容(包括可能的" > "looptemplate匹配。除非您将>添加到可选组以及looptemplate

    \{\{(?<field>[^>]*?)(?: > (?<looptemplate>.*?))?\}\}
    

    最后一点说明。这只是一个品味问题,但让我向您介绍一种不同形式的逃避。在角色类内部(仅]-^\仍然是)时,许多元字符都不是元字符。所以你可以将你的元字符包装在一个字符类中以逃避它:

    [{][{](?<field>[^>]*?)(?: > (?<looptemplate>.*?))?[}][}]
    

    正如我所说,只是一个建议,但对于大多数角色,我发现这比使用反斜杠更具可读性。

答案 1 :(得分:2)

  

另外,我检查了“SingleLine”,因为我想要。匹配\ n

如果取消勾选“单行”,它将起作用。显然你的.就是问题所在。一个简单的解决方案是使用.*?代替.*,因为它会非贪婪地选择所需的内容(而不是贪婪地尽可能多地选择)。另一个解决方案是将.替换为更具体的内容,例如负面预测,因为您可能不希望匹配其中的另一个{{(甚至是}})。但在这种情况下,非贪婪的解决方案要容易得多。

你应该更改字段字符类的乘数,这样它就不会匹配已经成为looptemplate一部分的东西。

另请注意,[ > ]是一个将选择空格或>的字符类。所以它不会选择" > "。如果你想要,只需关闭括号:

\{\{(?<field>[^>]*?)? > (?<looptemplate>[^}].*?)?\}\}

在你的情况下,因为你可能想让looptemplate是可选的,你可能想要这样做,但是有一个非捕获组:

\{\{(?<field>[^>]*?)?(?: > (?<looptemplate>[^}].*?))?\}\}

还有一个最后的注释;如果您希望.匹配换行符,请更好地提供必要的示例。

(好的,另一个注意事项,正如m.buettner在他的回答中正确提到的,字符类只需要提一次每个字符;此外,你不需要在字符类中转义花括号,所以这一切都简化为{ {1}})

答案 2 :(得分:0)

这个怎么样:

\{\{.*?\}\}

.*?.*类似,但使用了懒惰匹配,而不是贪婪。这意味着它会停止匹配,并尝试尽快继续匹配其余的正则表达式,而不是在继续使用正则表达式的其余部分之前尽可能多地尝试消耗的贪婪匹配。

因此,适用于:“{{this}}和}}}”

\{\{.*?\}\}匹配“{{this}}”

\{\{.*\}\}匹配“{{this}}和}}}”

答案 3 :(得分:0)

开始编辑:

好的,所以我改变了文本文件......

<div class="nest-1-2">
    <dl>
    <dt>Type:</dt>
    <dd>{{(Entity)Field Name.separator(, ) > [:Name:]
    foo came up
    boo is here too}}</dd>
    <dt>At:</dt>
    <dd>{{(Entity)Field Name > [:Name:]}}</dd>
    <dt>Team:</dt>
    <dd>{{(Entity)Field Name.separator(, ) > [:First Name:] [:Last Name:]}}</dd>
    </dl>
</div>

然后我在Regex新构造函数中添加了一个参数...... 具有讽刺意味的是,选项是“SingleLine”

System.Text.RegularExpressions.Regex Y = new System.Text.RegularExpressions.Regex("{{(.*?)\\}}", System.Text.RegularExpressions.RegexOptions.Singleline);

编辑结束 .... ... ...

我将您的示例字符串复制并粘贴到一个平面文本文件中进行测试....

namespace a
{
    class Program
    {
        static void Main(string[] args)
        {
            string X = System.IO.File.ReadAllText("C:\\Users\\rnirnberger\\Documents\\a.txt");
            System.Text.RegularExpressions.Regex Y = new System.Text.RegularExpressions.Regex("{{(.*?)\\}}");
            System.Text.RegularExpressions.MatchCollection Z = Y.Matches(X);
            foreach (System.Text.RegularExpressions.Match match in Z)
            {
                Console.WriteLine(match.Value);

                //If you want to strip out the double-braces
                //↓↓↓↓

                //Console.WriteLine(match.Value.Replace("{{", "").Replace("}}", ""));
            }
        }
    }