JAVA:使用XmlStreamReader收集xml标记的字节偏移量

时间:2010-07-04 23:51:58

标签: java xml stax

有没有办法使用XMLStreamReader准确收集xml标签的字节偏移量?

我有一个需要随机访问的大型xml文件。我不想将整个事情写入数据库,而是希望使用XMLStreamReader运行一次以收集重要标记的字节偏移量,然后能够使用RandomAccessFile稍后检索标记内容。

XMLStreamReader似乎没有办法跟踪字符偏移。相反,人们建议将XmlStreamReader附加到跟踪已读取的字节数的读取器(例如, apache.commons.io 提供的CountingInputStream)

e.g:

CountingInputStream countingReader = new CountingInputStream(new FileInputStream(xmlFile)) ;
XMLStreamReader xmlStreamReader = xmlStreamFactory.createXMLStreamReader(countingReader, "UTF-8") ;


while (xmlStreamReader.hasNext()) {
    int eventCode = xmlStreamReader.next();

    switch (eventCode) {
        case XMLStreamReader.END_ELEMENT :
            System.out.println(xmlStreamReader.getLocalName() + " @" + countingReader.getByteCount()) ;
    }

}
xmlStreamReader.close();

不幸的是必须进行一些缓冲,因为上面的代码打印出几个标签的相同字节偏移量。有没有更准确的方法来跟踪xml文件中的字节偏移(理想情况下不需要放弃正确的xml解析)?

6 个答案:

答案 0 :(得分:2)

你可以在XMLStreamReader(或XMLEvent.getLocation()上使用getLocation(),如果你使用XMLEventReader),但我记得在某处读它不可靠和精确。看起来它给出了标签的端点,而不是起始位置。

我有类似的需要准确地知道文件中标签的位置,我正在查看其他解析器,看看是否有一个可以保证提供必要的位置精度级别。

答案 1 :(得分:1)

您可以在实际输入流周围使用包装器输入流,只需将实际I / O操作延迟到包装流,但保留内部计数机制以及各种代码来检索当前偏移量?

答案 2 :(得分:1)

不幸的是,Aalto没有实现LocationInfo接口。

最后一个java VTD-XML ximpleware实现,目前为2.11 http://sourceforge.net/projects/vtd-xml/files/vtd-xml/ 每次调用后都会提供一些代码来保证字节偏移量 其IReader实现的getChar()方法。

各种字符编码的IReader实现 在VTDGen.java和VTDGenHuge.java中可用

为以下编码提供了IReader实现

ASCII;
ISO_8859_1
ISO_8859_10
ISO_8859_11
ISO_8859_12
ISO_8859_13
ISO_8859_14
ISO_8859_15
ISO_8859_16
ISO_8859_2
ISO_8859_3
ISO_8859_4
ISO_8859_5
ISO_8859_6
ISO_8859_7
ISO_8859_8
ISO_8859_9
UTF_16BE
UTF_16LE
UTF8;   
WIN_1250
WIN_1251
WIN_1252
WIN_1253
WIN_1254
WIN_1255
WIN_1256
WIN_1257
WIN_1258

使用getCharOffset()方法更新IReader 并实施它 通过将charCount成员添加到的偏移成员 VTDGen和VTDGenHuge课程 并且通过在每个IReader实现的每个getChar()和skipChar()调用时递增它应该为您提供解决方案的开始。

答案 3 :(得分:0)

我想我找到了另一种选择。如果用以下内容替换switch块,它将在结束元素标记之后立即转储位置。

        switch (eventCode) {
        case XMLStreamReader.END_ELEMENT :
            System.out.println(xmlStreamReader.getLocalName() + " end@" + xmlStreamReader.getLocation().getCharacterOffset()) ;
        }

此解决方案还要求必须手动计算结束标记的实际开始位置,并且具有不需要外部JAR文件的优势。

我无法追踪数据管理中的一些细微不一致(我认为这与我初始化XMLStreamReader的方式有关),但随着读者移动,我总是看到位置的不断增加通过内容。

希望这有帮助!

答案 4 :(得分:0)

我最近在How to find character offsets in big XML files using java?上找到了类似问题的解决方案。我认为它提供了一个基于ANTLR生成的XML-Parser的良好解决方案。

答案 5 :(得分:0)

我为此花了一个个漫长的周末,由于这里的一些线索,部分到达了解决方案。值得注意的是,自从OP发布这个问题以来的10年里,我认为这并没有变得那么容易。

TL; DR使用Woodstox和字符偏移量

第一个要解决的问题是,大多数XMLStreamReader实现在您要求它们当前的偏移量时似乎提供的结果都不准确。 Woodstox在这方面似乎是坚如磐石。

第二个问题是您使用的偏移量的实际类型。不幸的是,如果您需要使用多字节字符集,则似乎必须使用char偏移量,这意味着从文件中进行随机访问检索不会非常有效-您不能仅将指针设置为偏移处的文件并开始读取,您必须通读直到到达偏移处,然后开始提取。 也许我没有做过一种更有效的方法,但是这种情况对于我的情况是可以接受的。 500MB的文件非常灵活。

[edit]因此,这变成了我脑中裂开的事情之一,我最终写了一个FilterReader,它在读取文件时保留了字节偏移量到char偏移量映射的缓冲区。当需要获取字节偏移量时,我们首先向Woodstox请求char偏移量,然后让自定义阅读器告诉我们char偏移量的实际字节偏移量。我们可以从元素的开头和结尾获取字节偏移量,从而提供我们需要输入的内容,并通过将其作为RandomAccessFile打开将其从文件中以手术方式提取出来。

我为此创建了一个库,它位于GitHubMaven Central上。如果您只想获取重要的信息,请参加ByteTrackingReader中的聚会小窍门。 [/ edit]

another similar question on SO about this(但是被接受的答案使我感到恐惧和困惑),有些人评论说这整件事是个坏主意,为什么要这么做? XML是一种传输机制,您应该将其导入数据库并使用更合适的工具处理数据。在大多数情况下,这是正确的,但是如果您要构建通过XML进行通信的应用程序或集成(在2020年仍将继续发展),则需要使用工具来分析和操作交换的文件。我每天都会收到请求来验证提要内容,并能够从海量文件中快速提取出一组特定的项目,并且不仅要验证内容,而且还必须验证格式本身。

无论如何,希望这可以节省一个人几个小时,或者至少使他们更接近解决方案。如果您在2030年发现这个问题,请上帝帮助您,尝试解决相同的问题。