Java正则表达式提取标签之间的文本

90
我有一个文件,其中包含一些自定义标签,我想编写一个正则表达式来提取标签之间的字符串。例如,如果我的标签是:
[customtag]String I want to extract[/customtag]

我该如何编写一个正则表达式来提取标签之间的字符串。这段代码似乎是朝着正确方向迈出的一步:

Pattern p = Pattern.compile("[customtag](.+?)[/customtag]");
Matcher m = p.matcher("[customtag]String I want to extract[/customtag]");

不确定接下来该做什么。有什么想法吗?谢谢。


1
首先,您需要转义[]方括号,因为它们是正则表达式中的元字符。 - ridgerunner
8个回答

199

你已经在正确的轨道上。现在你只需要提取所需的组,如下:

final Pattern pattern = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);
final Matcher matcher = pattern.matcher("<tag>String I want to extract</tag>");
matcher.find();
System.out.println(matcher.group(1)); // Prints String I want to extract

如果你想提取多个结果,可以尝试这样做:

public static void main(String[] args) {
    final String str = "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear</tag>";
    System.out.println(Arrays.toString(getTagValues(str).toArray())); // Prints [apple, orange, pear]
}

private static final Pattern TAG_REGEX = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);

private static List<String> getTagValues(final String str) {
    final List<String> tagValues = new ArrayList<String>();
    final Matcher matcher = TAG_REGEX.matcher(str);
    while (matcher.find()) {
        tagValues.add(matcher.group(1));
    }
    return tagValues;
}

然而,我同意正则表达式在这里并不是最好的答案。我会使用XPath来查找我感兴趣的元素。详情请参见Java XPath API


3
非常感谢,这正是我需要的。我会研究XPath,但现在我认为这个解决方案可以工作。我的应用程序非常简单,可能会一直保持这种状态。再次感谢! - b10hazard
这个字符串 "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear" 怎么办?我们怎样才能在没有关闭标签的情况下获取 pear - K.Sopheak
通用化: private String extractDataFromTags(String tag) { Pattern pattern = Pattern.compile("<.+?>(.+?)</.+?>"); Matcher matcher = pattern.matcher(tag); matcher.find(); return (matcher.group(1)); // 输出我想要提取的字符串或抛出异常 } - TroniPM

17

说实话,对于这种类型的解析,正则表达式并不是最好的选择。你发布的正则表达式可能适用于简单情况,但如果情况变得更加复杂,你将遇到巨大问题(与无法可靠地使用正则表达式解析HTML的原因相同)。我知道你可能不想听这个,当我问类似的问题时,我也不想听,但是在我停止尝试为所有内容使用正则表达式后,字符串解析变得更加可靠。

JTopas是一个很棒的分词器,可以轻松手动编写解析器(我强烈建议使用jTopas而不是标准的Java扫描器/等库)。如果您想看到jTopas的实际应用,这里是一些我使用jTopas编写的解析器,用于解析类型的文件

如果您正在解析XML文件,则应使用XML解析器库。除非您只是出于兴趣而这样做,否则不要自己解析,因为有许多经过验证的选项可供选择


谢谢您的建议。我已经将它们加入书签,并且在未来的项目中一定会考虑使用它们。目前,由于我要解析的文件非常小/简单,所以正则表达式方法可能是我要选择的方法。 - b10hazard

8
一个通用、简单且有点原始的方法来查找标签、属性和值
    Pattern pattern = Pattern.compile("<(\\w+)( +.+)*>((.*))</\\1>");
    System.out.println(pattern.matcher("<asd> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd TEST</asd>").find());
    System.out.println(pattern.matcher("<asd attr='3'> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd> <x>TEST<x>asd>").find());
    System.out.println("-------");
    Matcher matcher = pattern.matcher("<as x> TEST</as>");
    if (matcher.find()) {
        for (int i = 0; i <= matcher.groupCount(); i++) {
            System.out.println(i + ":" + matcher.group(i));
        }
    }

如果有一系列不同的标签或嵌套标签,例如<h2>Mac</h2><h1>喜欢它</h1><h2>Mac<h1>喜欢你的回答</h1></h2>,那么模式会是什么? - Ram
1
请编辑 i < matcher.groupCount(); 为 i <= matcher.groupCount();,以包括第一个匹配子字符串,即位于索引0处。 - AVA

5
    String s = "<B><G>Test</G></B><C>Test1</C>";

    String pattern ="\\<(.+)\\>([^\\<\\>]+)\\<\\/\\1\\>";

       int count = 0;

        Pattern p = Pattern.compile(pattern);
        Matcher m =  p.matcher(s);
        while(m.find())
        {
            System.out.println(m.group(2));
            count++;
        }

4

试试这个:

Pattern p = Pattern.compile(?<=\\<(any_tag)\\>)(\\s*.*\\s*)(?=\\<\\/(any_tag)\\>);
Matcher m = p.matcher(anyString);

例如:
String str = "<TR> <TD>1Q Ene</TD> <TD>3.08%</TD> </TR>";
Pattern p = Pattern.compile("(?<=\\<TD\\>)(\\s*.*\\s*)(?=\\<\\/TD\\>)");
Matcher m = p.matcher(str);
while(m.find()){
   Log.e("Regex"," Regex result: " + m.group())       
}

输出:

10 Ene

3.08%


2
    final Pattern pattern = Pattern.compile("tag\\](.+?)\\[/tag");
    final Matcher matcher = pattern.matcher("[tag]String I want to extract[/tag]");
    matcher.find();
    System.out.println(matcher.group(1));

如果前缀是动态的,那么标签的前缀怎么样? - user1514499

1

我在回复中提前说明一点:"你不应该使用正则表达式来解析XML - 这只会导致无法正确处理的边缘情况,而且当你尝试修复它时,正则表达式的复杂度会不断增加。"

话虽如此,你仍然需要通过匹配字符串并获取所需的组来进行操作:

if (m.matches())
{
   String result = m.group(1);
   // do something with result
}

0

这对我有效,将其用于您的主方法下的Scanner输入。也适用于Hackerrank“标记内容提取器”。

  boolean matchFound = false;
        Pattern r = Pattern.compile("<(.+)>([^<]+)</\\1>");
        Matcher m = r.matcher(line);

        while (m.find()) {
            System.out.println(m.group(2));
            matchFound = true;
        }
        if ( ! matchFound) {
            System.out.println("None");
        }
        
        testCases--;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接