使用.NET正则表达式查找不在字符串内的内容

时间:2012-01-23 02:06:40

标签: .net regex

我希望使用.NET正则表达式找到不在双引号内的每个单词。这是一些示例文本:

Hello world I want to get all of these words as a match "but not these ones...
because they're inside a string. And maybe I'll \"escape\" the quotes too." Also,
these words should match. Now we're outside of the string. And I can't escape
quotes; \"this still shouldn't be matched."

所以我想要匹配:

Hello, world, I, want, to, get, all, of, these, words, as, a, match, Also,
these, words, should, match, Now, we, re, outside, of, the, string, And, I,
can, t, escape, quotes

这是否可以使用.NET regex外部堆栈和断言?我已经走到了这一步:

(?<=(?(rstack)|(?!))(?<-rstack>").*?(?<rstack>").*?)\w+... same thing for fstack

'当然,它不起作用。

2 个答案:

答案 0 :(得分:2)

我认为,不是匹配引号外的单词,而是匹配引号内的单词,并用''替换它们。

在某种程度上,我建议您查看this question和@ RicardoNolde的回答:

(?>(?(STR)(?(ESC).(?<-ESC>)|\\(?<ESC>))|(?!))|(?(STR)"(?<-STR>)|"(?<STR>))|(?(STR).|(?!)))+

(请参阅他的问题,以获得比我能做的更好的解释,因为我不熟悉.NET引擎)。

这匹配引号内的所有单词。如果你删除它们(即替换为''),然后将结果字符串与@"\b(\w+)\b"匹配,那么你就是对的。

然而除非您的字符串中包含,否则您将遇到问题

  • 所有引用对都是格式良好的(即整个文本中的偶数引号)
  • 所有报价对匹配(即没有\"与对应的"相似)
  • 对任何嵌套引号进行转义("This is a quote that contains another "quote", tricky!"可以在引号内包含"This is a quote that contains another "", tricky!"

(之前的正则表达式似乎适用于\"this still shouldn't be matched"的示例,但如果将其更改为"this still shouldn't be matched\" but this should. "hi",则会遇到问题,因为内部\"被视为逃脱报价而不是平衡货币对的一部分。)


话虽如此,如果 你的文字符合我上面提到的那三条规则,你可以用普通的正则表达式做你想做的事情(尽管我觉得既然你使用的是.NET,你也可以这么做利用其堆栈功能):

(?<!")\b[a-zA-Z]+\b(?=(?>((\\"|[^"])*)"(?>(\\"|[^"])*)")*(\\"|[^"])*$)

这意味着“匹配任何单词后跟偶数个未转义的引号。” 逻辑是,由于引号已配对,如果您不在一组引号内,则会有偶数(未转义)引号。

在行动here中查看它((?>...)是为了避免正则表达式引擎执行不必要的反向跟踪,以便提高性能。 (注意:我将您不匹配的引号\"this still shouldn't be matched"更改为"this still shouldn't be matched",以便输入符合上述三条规则。)

另请注意,您不能说“匹配任何单词后跟偶数引号”(包括转义的引号),因为您将遇到嵌套引号匹配内的单词问题。例如,Hello world "this is a quote \"containing another quote\" end quote" goodbye将错误地使内部another quote与正则表达式匹配,因为字符串中仍有偶数引号。

总结

真的需要所有引用对都是格式正确/匹配的嵌套引号要转义才能使任何类型的正则表达式工作,.NET引擎或不

我建议使用@ RicardoNolde在其他问题(上面链接的)中的答案来删除所有引用的文字,然后匹配所有剩余的单词。

答案 1 :(得分:1)

此表达式使用平衡组返回所需的单词。匹配表达式后,引号内的单词可以作为m.Groups["word"].Captures.OfType<Capture>.Select(c=>c.Value)访问。通过在模式中包含可选的断言,如果引号不平衡,匹配可能会失败;如果从表达式中删除,则忽略无关的引号。

以下是包含模式并打印所需输出的驱动程序。

string input = @"Hello world I want to get all of these words as a match ""but not these ones...  because they're inside a string. And maybe I'll \""escape\"" the quotes too."" Also,  these words should match. Now we're outside of the string. And I can't escape  quotes; \""this still shouldn't be matched.""";
string pattern = @"(?>
                     ^(?:
                       #capture word only if not inside a quotation
                       (?(quote)\w+|(?<word>\w+))
                         (?:
                           ([^\w""]*|$)
                             (?(quote)
                                  #if within a quote, close unless escaped
                                  (?:(?<=\\)\""|(?<-quote>(?<!\\)\""))
                                  |
                                  #if not within a quote, open quote
                                  (?<quote>\"")
                             )?
                         )*
                       )*
                     )$
                     (?(quote)(?!)) # will fail to match if extra quotes
                                    # if line removed, will ignore extra quote";

RegexOptions options = RegexOptions.IgnorePatternWhitespace;

Match m = Regex.Match(input, pattern, options);
if (!m.Success) Console.WriteLine("Failed");
else
    foreach (
      var word in m.Groups["word"]
                   .Captures
                   .OfType<Capture>()
                   .Select(a => a.Value))
           Console.WriteLine(word);