【问题标题】:What is the Java regex to use back-references and capture groups correctly什么是正确使用反向引用和捕获组的 Java 正则表达式
【发布时间】:2011-06-30 19:55:37
【问题描述】:

我想从消息中剥离 SOAP 信封以获取正文中的 XML。

我尝试了以下方法;

String strippedOfEnvelopedHeader = msg.replaceAll("(?s)(?i)<(.*):Envelope.*<\1:Body>", "");

我认为这会从像这样的消息中剔除 SOAP 信封,特别是标头;

<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>
<env:Header xmlns:env='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'/>
<soapenv:Body>
<myXML> stuff is here</myXML>
</soapenv:Body>
</soapenv:Envelope>

这应该导致;

<myXML> stuff is here</myXML>
</soapenv:Body>
</soapenv:Envelope>

但是,组反向引用似乎不起作用。

如果我同时替换捕获组和反向引用,则替换工作正常;

String strippedOfEnvelopeHeader = msg.replaceAll("(?i)(?s)<soapenv:Envelope.*<soapenv:Body>", "");

我想我可以猜到问题所在,捕获组贪婪并抓取了整个消息,因此匹配失败。

但解决方案却躲开了我。

有什么想法吗?

【问题讨论】:

  • argh 正则表达式来解析标记....我不知道肥皂,但它看起来像是 xml 本身?如果是这样,请使用 xml 解析器。

标签: java xml regex


【解决方案1】:

试试这个:

String strippedOfEnvelopedHeader = msg.replaceAll("(?s)<(\\w+):Envelope[^<>]*>.*?<\\1:Body>", "");

关键点:

  1. 正如其他人已经指出的那样,Java 字符串中的反斜杠需要转义。因此,在将正则表达式格式化为 Java 字符串时,正则表达式中的每个反斜杠都会变成双反斜杠。
  2. 你是using the dot inappropriately。您不能将 任何字符 作为 XML 命名空间。 XML 标记内不能有任何字符。通过使用(否定的)字符类使您的正则表达式更加具体,您将轻松避免.* 吃得过多的问题。我在我的正则表达式中留下了一个.*?,因为我不知道你将使用这个正则表达式的所有其他文本的结构。但是,如果它总是有一个 &lt;env:Header&gt; 元素,那么您应该将我的正则表达式中的 .*? 替换为 \s*&lt;env:Header[^&lt;&gt;]*&gt;\s* 或任何足够具体的内容,以避免失控匹配,同时仍然匹配您想要的所有内容。

如果你也想删除结束标签,试试这个:

String strippedOfEnvelopedHeader = msg.replaceAll("(?s)<(\\w+):Envelope[^<>]*>.*?<\\1:Body>\\s*(.*?)\\s*</\\1:Body>\\s*</\\1:Envelope>", "$2");

在这个正则表达式中,第二个.*? 是合适的,如果你想删除标签,不管标签里面有什么。

【讨论】:

    【解决方案2】:

    作为一个方面,你为什么不尝试摆脱整个soap消息包装器?

    String strippedOfEnveloped = msg.replace( "^ (?six) &lt; (.*):Envelope .* &lt;\\1:Body&gt; (.*) &lt;/\\1:Body&gt; .* $", "\\2" );

    【讨论】:

    • OP 使用的是 Java,而不是 Perl 或 PHP。在 Java 中,单引号文字总是创建一个字符,如 C 和 C++。
    • @KennyTM - 好吧,我是这么认为的。谢谢。编辑,扩展修饰符也可用吗?即:(?x)
    • 嘿。我喜欢这个解决方案,在一个语句中完成所有事情,但我的问题真的完全忘记了 \1 确实在 Java 中转义。 Java 中的正确替换是 $2 而不是 \\2。即 msg.replaceAll("(?si)(.*)\\1:Body>.*", "$2")
    • @graney,在 Perl 中,我会在替换端使用 $2。 \\2 在替换方面是 sed 怪胎。我有时会在 SO 上看到 \\2 甚至用于 PCRE 实现。我认为Java是倒退的。对不起。
    • @snl:哦,Java 倒退了,只是不是你想的那样。任何像 Java 的正则表达式那样让您对多级评估感到困惑的事情就像试图在 csh 中编程一样愚蠢。试试 Groovy。或 Perl。或者红宝石。或其他任何东西。
    【解决方案3】:

    尝试 2 个反斜杠

    "(?si)<(.*):Envelope.*<\\1:Body>"
    

    您需要 2,因为 \1 本身已经是 Java 的特殊转义序列。因此,在输入正则表达式引擎之前,它将被解码为字符 U+0001。您需要通过添加一个反斜杠来保护它。

    (然后是通常的“不要使用正则表达式解析 XML”警告..​​.)

    【讨论】:

    • 是的。我讨厌为此使用正则表达式。但是我一直在寻找一种方法来解决使用 Axis 或 JAX-WS 阻塞的一堆遗留 jar 依赖项。不过,我让 JAXB 为身体工作,所以这是我黑客的极限。 [加号9个月后更换]
    • 我发现了一种使用 org.w3c.dom.Document 的“更安全”的方式。 JAXB unmarshaller 可以获取一个 DOM Node 对象,因此我只需使用 DOM 获取 Body 元素的内容,然后将此节点传递给 unmarshaller。当然,这是超过 20 行代码、一堆额外的导入和一个 try-catch,而正则表达式是一行代码。
    猜你喜欢
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    • 2020-02-13
    • 1970-01-01
    • 2012-11-06
    • 2018-07-13
    相关资源
    最近更新 更多