【问题标题】:Regex: Match a string up to a pattern (Which may not exist) [duplicate]正则表达式:将字符串与模式匹配(可能不存在)[重复]
【发布时间】:2013-06-09 22:27:19
【问题描述】:

我正在尝试使用 Python 中的正则表达式标记器解析 XML 文档(这是一个有限集,所以正则表达式就可以了!),但我无法正确匹配 cmets。

这些 cmets 的格式为 <!--This is a comment--> 形式,其中注释本身可以包含各种非字母数字字符(包括“-”)

我想以这样一种方式匹配它们,将评论分成以下标记:

<!--

This is a comment

-->

开始标记很容易获得,我用另一个正则表达式成功地抓住了它,但是评论正则表达式本身变得太贪婪并从结束标记中抓取--。不过,我希望这个正则表达式能够获取不一定包含在注释中的字符串,因此它也应该能够采用 <Tag>This is text</Tag> 并正确返回 This is text

这是我目前用于文本的正则表达式:

[^<>]+(?!-->)

最终结果是This is a comment--,而我只想要This is a comment,以便我的其他正则表达式可以获取-->。这个正则表达式确实适用于普通标签,但是,由于结束标签上存在“This is text。

我知道我不能正确使用负前瞻。关于我在这里做错了什么的任何想法?我试过[^<>]+(?=-->),但是这与任何不是这种形式的评论(如普通标签)不匹配。我认为(?!-->) 在看到该模式时会停止匹配,但它似乎不像那样工作,只是继续匹配直到它看到结尾 '>'。

为上下文贴一段代码:

xml_scanner = re.Scanner([
    (r"  ",             lambda scanner,token:("INDENT", token)),
    (r"<[A-Za-z\d._]+(?!\/)>",  lambda scanner,token:("BEGINTAG", token)),
    (r"<\/[A-Za-z\d._]+(?!\/)>",  lambda scanner,token:("ENDTAG", token)),
    (r"<[A-Za-z\d._]+\/>",      lambda scanner,token:("INLINETAG", token)),
    (r"<!--",               lambda scanner,token:("BEGINCOMMENT", token)),
    (r"-->",                lambda scanner,token:("ENDCOMMENT", token)),
    (r"[^<>]+(?!-->)",         lambda scanner,token:("DATA", token)),
    (r"\r$", None),
])

for line in database_file:
    results, remainder = xml_scanner.scan(line)

这是脚本目前唯一要做的事情。

【问题讨论】:

  • 通过在结果中包含--,正则表达式确保匹配没有被--&gt;...comment 跟在另一方面, 后面跟着--&gt;,所以你的结果不能以此结尾。
  • 啊。有没有办法让它检测到整个模式而不包括它?

标签: python regex negative-lookahead


【解决方案1】:

你的问题是,在[^&lt;&gt;]+(?!--&gt;)中,先行断言只有在This is a comment--匹配后才会尝试,当然它会成功,因为在正则表达式引擎结束的位置没有--&gt;

因此,您必须在字符串的每个 位置检查前瞻。正确的成语通常是:

(?:(?!-->).)*

或者,在你的情况下

(?:(?!-->)[^<>])*

这匹配除尖括号外的任意数量的字符,直到出现--&gt; 或到达字符串的末尾。

【讨论】:

  • 啊哈。我没有意识到正则表达式会这样工作。首先进行前瞻似乎有效。谢谢!
  • 这与使用积极的前瞻相比如何(如当前答案所示)?对每个位置进行负前瞻时,是否存在明显的性能差异?
  • 实际上,刚刚运行了一些时序测试,这种方法的时序明显低于正向前瞻(并且工作更干净)。这对我来说毫无意义,但看起来这是更好的解决方案。
  • 如果你想在否定前瞻表达式之前捕获东西(在捕获组中,为了使用 backrefs)怎么办?
  • @2rs2ts:只需在其周围添加一组括号:((?:(?!--&gt;)[^&lt;&gt;])*)
【解决方案2】:

您需要使用肯定前瞻,以确保此模式领先于您的匹配。

[^<>]+(?=-->)

如果你想让它更通用,以便它也匹配其他标签的内容,你可以在前瞻断言中使用替代:

[^<>]+(?=-->|</)

here on Regexr

如果前面有“--&gt;”或“&lt;/”,此正则表达式将停止。

【讨论】:

  • 虽然这绝对适用于 Regexr,但它并没有为我抓取标签之间的完整文本。它抓住了除了最后一个字符之外的所有东西。所以,对于&lt;!--This is a comment--&gt;,它正在抓取This is a commen。同样适用于标签。不过,也许在其他地方还有另一个错误......
  • @Austin,那么您应该发布您的代码、使用正则表达式的位置以及如何检索结果。
  • 我已经发布了上下文代码。
  • 好的,我不确定我做了什么,但我似乎已经修复了正在发生的错误。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多