4

我需要从大约 100 个最长 200,000 行的 XML 文件中读取前 15 行。有没有办法使用像BufferedReader这样的东西来有效地做到这一点?此问题中概述的步骤使用DocumentBuilder.parse(String); 这会尝试一次解析整个文件。

编辑:前 15 个元素包含有关我想解析为表格的文件的元数据(页面名称、上次编辑日期等)。

4

5 回答 5

7

这可能是您想要做的 - 正如我在评论中所写,使用 SAX 解析器,当满足您的停止条件时使用它

如何随时停止使用 SAX 解析 xml 文档?

编辑:

测试.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <first>
        <inner>data</inner>
    </first>
    <second>second</second>
    <third>third</third>
    <next>next</next>
</root>

ReadXmlUpToSomeElementSaxParser.java

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ReadXmlUpToSomeElementSaxParser extends DefaultHandler {

    private final String lastElementToRead;

    public ReadXmlUpToSomeElementSaxParser(String lastElementToRead) {
        this.lastElementToRead = lastElementToRead;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        // just for showing what is parsed
        System.out.println("startElement: " + qName);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (lastElementToRead.equals(qName)) {
            throw new MySaxTerminatorException();
        }
    }

    public static void main(String[] args) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();

        try {
            saxParser.parse("src/test.xml", new ReadXmlUpToSomeElementSaxParser("second"));
        } catch (MySaxTerminatorException exp) {
            // nothing to do, expected
        }
    }

    public class MySaxTerminatorException extends SAXException {
    }

}

输出

startElement: root
startElement: first
startElement: inner
startElement: second

为什么这样更好?仅仅因为某些应用程序可以向您发送

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <first><inner>data</inner></first>
    <second>second</second>
    <third>third</third>
    <next>next</next>
</root>

面向线的方法将失败...

我提供了不计算元素的解析器,以表明可以根据实现...所需的业务逻辑来定义条件。

字符()警告

要读取元素中的数据,您可以使用character()方法,但请注意

SAX 解析器可以在单个块中返回所有连续的字符数据,也可以将其拆分为多个块

在JavaDoc中阅读更多内容

于 2014-04-28T15:20:34.720 回答
4

Here's a simple solution that will read your file line by line until it stores 15 lines worth of data in the lines variable (Or less than 15 if your file is smaller).

File f = new File("your path");
BufferedReader br = null;
String lines = "";
try
{
    br = new BufferedReader(new FileReader(f));
    String line = null;
    int lineCount = 0;
    while((line = br.readLine()) != null)
    {
        lineCount++;
        lines += line + "\n";
        if(lineCount == 15) break;
    }
}
catch(Exception e)
{
    e.printStackTrace();
}
finally
{
    try{br.close();}catch(Exception e){}
}
于 2014-04-28T15:16:26.017 回答
4

我建议研究一个流式 XML 解析器;流式 API 的用例扩展到读取数百 GB 的文件,这些文件显然无法放入内存。

在 Java 中,StAX API 是原生 SAX API 的(相当大的)演变。在此处查看有关“即时”解析的教程:

http://tutorials.jenkov.com/java-xml/stax.html

于 2014-04-28T15:20:33.883 回答
2

最好像下面这样手动阅读。在您的情况下,DOM 解析器会很昂贵。如果您真的想解析 xml 并提取/插入节点,您可以使用 SAX 解析器。

try (BufferedReader br = new BufferedReader(new FileReader("C:\\testing.txt")))
{

    String sCurrentLine;

    while ((sCurrentLine = br.readLine()) != null) {
        System.out.println(sCurrentLine);
    }

} catch (IOException e) {
    e.printStackTrace();
} 
于 2014-04-28T15:17:31.770 回答
2

假设您想阅读如下内容:

<?xml ...?>
<root>
    <element>data</element>
    ...
    <otherElement>more data</otherElement>
    <ignoredElement> ... </ignoredElement>
    ... more ignored Elements
</root>

并且您只需要 root 中的前 13 个子元素(恰好在您的超大文件的前 15 行中)。

您可以使用 SAX 解析器来读取文件,并在读取这些元素后立即中止它。

您可以使用标准 J2SE 设置 SAX 解析器:

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader reader = sp.getXMLReader();

然后你需要创建一个ContentHandler类作为你的数据处理程序。我会打电话DataSaxHandler的。如果您扩展DefaultHandler,您只需要实现您感兴趣的方法。这是一个示例,您可以将其用作起点。它将检测每个元素的开始和结束并将其打印出来。它将计算 15 个结束标签(它不会生成格式良好的输出)并且它会忽略属性。以它为起点(我没有测试它):

public class DataSaxHandler extends DefaultHandler {

    private int countTags = 0;
    private boolean inElement = false;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        System.out.println("<" + qName + ">");
        inElement = true;
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        countTags++;
        System.out.println("</" + qName + ">");
        inElement = false;

        if(countTags > 15) {
            // throw some exception to stop parsing
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if(inElement) {
            System.out.println(new String(ch, start, length));
        }
    }
}

您将它注册到您的 SAX 阅读器并使用它来解析文件。

    reader.setContentHandler(new DataSaxHandler());
    reader.parse(new InputSource(new FileInputStream(new File(PATH, "data.xml"))));
于 2014-04-28T15:44:02.353 回答