【问题标题】:Partial read of xml file部分读取xml文件
【发布时间】:2014-06-14 05:08:25
【问题描述】:

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

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

【问题讨论】:

  • DocumentBuilder (DOM) 尝试解析所有内容。如果你想阅读lines,你应该使用BufferedReader。如果您想读取 tags 那么您应该使用 SAX (org.xml.sax) 阅读器(或 XML 阅读器),这将允许您按顺序阅读 XML 并响应由找到的标签引起的事件.
  • 一旦你有了 XML,试着把它读成 XML。我不确定这是否可能,但我建议修改 SAX 解析器 (mkyong.com/java/how-to-read-xml-file-in-java-sax-parser) 以在您读取前 15 个元素时结束,但请注意,即使是长 XML 也可以只在一行中......
  • 你可以统计startElement方法中读取的元素数量,当你读取到一定数量(元素数量,而不是行)时停止。
  • 我希望利用解析器带来的 xml 友好方法。如果我只使用 BufferedReader,我不需要手动分离我的元素吗?
  • 您可以使用 SAX 解析器并在 characters() 方法中计算换行符。但是如果你真的想从文件的开头提取一些东西,你可以在找到它时停止。

标签: java xml parsing file-io gosu


【解决方案1】:

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

How to stop parsing xml document with SAX at any time?

编辑:

test.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>

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

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

characters() 警告

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

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

阅读更多JavaDoc

【讨论】:

  • 最好在此处粘贴一些代码,因为该问题有一些不同的上下文。
  • 我认为它几乎相同且简单,但我提供了代码;-)
  • 您能否在上面的评论中添加链接以供将来参考?
  • 评论在一段时间后不可编辑,但在未来,人们会参考被赞成和接受的答案,以便他们找到答案;-)
【解决方案2】:

假设您想阅读如下内容:

<?xml ...?>
<root>
    <element>data</element>
    ...
    <otherElement>more data</otherElement>
    <ignoredElement> ... </ignoredElement>
    ... more ignored Elements
</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"))));

【讨论】:

    【解决方案3】:

    这是一个简单的解决方案,它将逐行读取您的文件,直到它在 lines 变量中存储 15 行数据(或者如果您的文件较小,则小于 15)。

    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){}
    }
    

    【讨论】:

    • 对你的代码做一个简单的解释,然后也许我会+1。 |=^]
    • 对我来说看起来很自我描述
    • 对我来说也一样,但对每个人来说都不一样。就是这样
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      您最好像下面这样手动阅读。在您的情况下,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();
      } 
      

      【讨论】:

      • 这取决于 OP 打算如何处理前 15 行。如果他们想解析 XML,他们应该使用流式解析器,即 SAX,它不会像 DOM 解析器那样加载整个文档。
      猜你喜欢
      • 1970-01-01
      • 2012-05-27
      • 1970-01-01
      • 1970-01-01
      • 2017-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      相关资源
      最近更新 更多