【问题标题】:Java Pattern regex to extract between tagsJava模式正则表达式在标签之间提取
【发布时间】:2017-05-25 08:37:34
【问题描述】:

我正在尝试为 RSS 源设计我的自定义 XML 阅读器。以下是我在测试中的 JAVA 代码:

Pattern pattern = Pattern.compile("<(item)(.*?)>((.*))</\\1>", Pattern.CASE_INSENSITIVE);

Matcher matcher = pattern.matcher("<item value=\"key\" atr='none'><title val=\"has value\">Good</title><link>www</link></item>"
+ "<item value=\"key\" atr='none'><title val=\"has value\">Bad</title><link>http</link></item>"
+ "<item value=\"key\" atr='none'><title val=\"has value\">Neutral</title><link>ftp</link></item>");

while (matcher.find()) {
for (int i = 0; i < matcher.groupCount(); i++) {
        System.out.println("\n" + i + ":" + matcher.group(i));
}}

这是输出:

0:<item value="key" atr='none'><title val="has value">Good</title><link>www</link></item><item value="key" atr='none'><title val="has value">Bad</title><link>http</link></item><item value="key" atr='none'><title val="has value">Neutral</title><link>ftp</link></item>

1:item

2: value="key" atr='none'

3:<title val="has value">Good</title><link>www</link></item><item value="key" atr='none'><title val="has value">Bad</title><link>http</link></item><item value="key" atr='none'><title val="has value">Neutral</title><link>ftp</link>

期望的输出:

<title val="has value">Good</title><link>www</link>
<title val="has value">Bad</title><link>http</link>
<title val="has value">Neutral</title><link>ftp</link>

基本上,我希望循环运行的时间与源字符串中存在的项目标签数量一样多。目前,正则表达式中的第 3 组正在提取字符串,直到与第 1 组匹配的最后一个结束标记,情况并非如此。第三组应包含字符串,直到匹配第一组的相应结束标记。

编辑: 根据@11thdimension 的建议,我正在添加一些我需要的更多信息:

  1. XML 结构还可以在 ITEM 标记中包含其他标记,例如日期、作者等。代码还应使用标题和链接标记检索这些标记。
  2. 标签的层次结构不固定。它们可以是任何顺序:标题、链接、日期或链接、标题、日期或日期、链接、标题等。

【问题讨论】:

  • 为什么不使用现有的 XML 解析器之一呢?正则表达式不是适合这项工作的工具...
  • @Lucero 我试过了,但是它们很慢并且需要太多开销。据我所知,JAVA 没有任何内置的 XML 解析器。我们必须依赖外部资源。此外,XML 结构可能因来源而异。如果发生这种情况,那么为具有不同标记名称的不同 XML 结构进行编码就不那么健壮了。所以我正在根据我的需要设计一个可以读取多个结构的简单 XML Parser。我并不是说我发展得最好,但它最适合我。
  • 我认为你错了。第一个 XML 支持已经与 Java 捆绑了很长时间(另请参阅 *.com/questions/9430392/…)。只要输入是有效的 XML,第二个 XML 解析器就完全独立于结构,这与基于正则表达式的解析器大不相同。第三,如果您想比较顺序解析性能,请不要将 XML 读入 DOM 表示,而是使用阅读器。
  • 至于你的正则表达式实现,永远不要在没有惰性限定符的情况下使用.* 这样的事情,你总是会自取其辱。请注意,. 可能与您期望的不匹配,除非您添加 DOTALL 标志。但这并没有改变将正则表达式用于标记化之外的根本缺陷;它无法处理递归和配对,因此在读取结构化数据时总会在某些时候让您失败。

标签: java regex xml pattern-matching


【解决方案1】:

您应该按照 Lucero 的建议使用 XML 解析器。

但是,如果您必须使用 RegEx,那么您可以使用以下。

<title.*?<\/link>

工作正则表达式101链接https://regex101.com/r/EWG2Io/2

编辑

对于需要 &lt;item&gt;&lt;/item&gt; 中的所有内容的特殊情况,请使用以下内容

<item.*?>(.*?)<\/item>

工作示例https://regex101.com/r/Ow1A5F/1

这里还有 Java 示例

public class TestRegex {
    public static void main(String[] args) {
        String str = "<item value=\"key\" atr='none'><date><date><title val=\"has value\">Good</title><link>www</link></item><item value=\"key\" atr='none'><title val=\"has value\">Bad</title><link>http</link><author></author></item><item value=\"key\" atr='none'><title val=\"has value\">Neutral</title><link>ftp</link></item>";

        Pattern pattern = Pattern.compile("<item.*?>(.*?)<\\/item>");

        Matcher match = pattern.matcher(str);

        while(match.find()) {
            System.out.println(match.group(1));
        }
    }
}

输出

<date><date><title val="has value">Good</title><link>www</link>
<title val="has value">Bad</title><link>http</link><author></author>
<title val="has value">Neutral</title><link>ftp</link>

【讨论】:

  • 你的解决方案不错,但也有一些例外。首先,如果结构被操纵,它不会给出预期的结果,例如ftp任何标题。其次,如果在链接标签之后的item标签中还有其他标签,它不会抓取它们。 any ftp2 月 12 日,它将跳过日期
  • 只要你只对&lt;title&gt;&lt;link&gt;感兴趣,它就会给出想要的结果,你认为它不会那么请在问题中添加特殊情况
  • @ 11thdimension,我根据您的建议添加了更多信息。谢谢我根据您的建议添加了更多信息。谢谢