【问题标题】:Finding things not within strings using .NET regular expressions使用 .NET 正则表达式查找不在字符串中的内容
【发布时间】:2012-02-16 12:01:20
【问题描述】:

我希望使用 .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 正则表达式外部堆栈和断言吗?我已经走到这一步了:

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

'当然,它不起作用。

【问题讨论】:

  • 如果没有平衡分组结构,似乎有一个简单的解决方案来获得所需的输出(删除\".*?(?:$|(?&lt;!\\)\") 的匹配项,然后在结果字符串中找到\w+ 的匹配项)。由于输入中没有平衡(任何地方的引号看起来都是合法的,没有嵌套,并且打开/关闭分隔符相同),在什么情况下断言会失败?
  • @drf:我希望永远不会(如果它们不平衡,请忽略它们)但几乎任何事情都可以。

标签: .net regex


【解决方案1】:

我认为与其匹配引号外的单词,不如匹配引号内的单词并将它们替换为''。

就此而言,我建议您看看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(?&gt;...) 是为了避免正则表达式引擎进行不必要的回溯,以便性能更好)。 (注意:我将您不匹配的引号 \"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 对另一个问题(上面链接)的回答来删除所有引用的文本,然后匹配所有剩余的单词。

【讨论】:

  • 我不希望使用替换方法,我可以使字符串符合这些规则并进行一些后处理,所以我会使用它。谢谢!
【解决方案2】:

此表达式使用平衡组来返回所需的单词。匹配表达式后,引号内的单词可以访问为m.Groups["word"].Captures.OfType&lt;Capture&gt;.Select(c=&gt;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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    相关资源
    最近更新 更多