【问题标题】:JAVA: gathering byte offsets of xml tags using an XmlStreamReaderJAVA:使用 XmlStreamReader 收集 xml 标签的字节偏移量
【发布时间】:2011-03-11 17:38:39
【问题描述】:

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

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

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

例如:

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 解析)?

【问题讨论】:

  • 我发现 StAX2 API 提供了非常有前途的 LocationInfo 类,方法是 getStartingByteOffset()getStartingByteOffset()。不幸的是,这些总是返回-1。有人熟悉 Stax2 和 Woodstox 吗?有什么方法可以配置阅读器以使此位置信息可用?
  • Dave:不幸的是,即使解析器是直接从 InputStream 构造的,当前的实现也无法获得实际的字节偏移量。因此,您最好的选择是使用 Reader 和字符偏移:这些都是可靠且有效的。这可能会产生额外的成本,但比尝试使用外部解决方案更可靠,其中问题是解析器的缓冲,这使得精确偏移相当困难。顺便说一句:Aalto 可以提供偏移量,但它的测试较少且功能较少。但它确实完全实现了基本的 Stax。
  • 顺便说一句,请随时在 Woodstox 用户或开发人员列表中提出这个问题;其他人可能有其他建议。我确实知道在很多地方都使用了字符偏移量(例如,用于编辑器等以指示错误位置)

标签: java xml stax


【解决方案1】:

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

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

【讨论】:

  • 这很接近,我可以处理获取标签的结尾而不是开始。最大的问题是它返回字符偏移而不是字节偏移。我的数据以 utf8(可变长度编码)编码,因此没有干净的方法可以从中获取字节偏移量。 >.
  • Location.getCharacterOffset() 的文档说“如果输入源是文件或字节流,那么这是该流中的字节偏移量,但如果输入源是字符媒体,那么offset 是字符偏移量。”所以也许可以构造和链接流,这样它实际上会给你一个字节偏移量。我正在考虑将文件输入流输入到 XML 事件流中的字节数组流?
  • 这听起来很狡猾,但祝你好运!我已经正式放弃了,只是将 xml 文件的内容存储在索引数据库中(想法是有准确的字节偏移量,这样我就可以使用 RandomAccessFile 来读取我需要的 xml 文件的部分,但是没有更长的时间似乎是个好主意。
  • 实际上,我认为@Gigatron 在他的评论中非常接近解决方案。基于stackoverflow.com/questions/15974196/…,您可以通过传递给createXMLStreamReader() 的内容来控制getCharacterOffset() 是字节偏移量还是字符偏移量。对于字节偏移,传递一个 Stream,对于 char 偏移,传递一个 Reader。由于 int 返回值,这仅适用于最多 2GB 的文件(可能是 4GB 有一些黑客攻击)。
  • 我知道这是一条老路,但我想确认已经说过的话:我一直在使用 Location 对象来获取 >50MB 文件的字符媒体上的字符偏移量,并且它有时会给出不正确的字符偏移量。大多数时候,它会提取整个标签内容,但是当我重复相同的操作时,它会跳过几个字符,而不是在相同的标签上,这让我想到了一个实现问题。
【解决方案2】:

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

【讨论】:

  • 我并没有完全遵循这一点,但 CountingInputStream 只是一个无缓冲 InputStream 的包装器,它记录已读取的字节数。听起来我已经在按照你的建议做?问题是 XMLStreamReader 似乎正在提前读取并进行缓冲。假设文件中有一个 500 字节的结束标记。 XMLStreamReader 可能会在读取 500 个字符、501 个或 600 个字符后触发 endElement 事件。
  • 是的,你是对的,没有正确思考。无论如何,接口中有一个 getLocation() 方法可能对您有用(如果解析器支持正确的 Location 对象)。 java.sun.com/webservices/docs/1.5/api/javax/xml/stream/…反过来位置提供:getCharacterOffset()
【解决方案3】:

很遗憾,Aalto 没有实现 LocationInfo 接口。

最后一个 java VTD-XML xipleware 实现,目前是 2.11 在sourceforgegithub 在每次调用后提供一些维护字节偏移量的代码 其 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() 调用上递增它应该给你一个解决方案的开始。

【讨论】:

    【解决方案4】:

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

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

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

    我无法找到数据管理中的一些细微不一致(我认为这与我初始化我的 XMLStreamReader 的方式有关),但随着读者通过内容。

    希望这会有所帮助!

    【讨论】:

    • 嗯,这很接近,除了它返回字符偏移量而不是字节偏移量。我的数据是用 utf8 编码的——可变长度编码——所以没有干净的方法可以从中获取字节偏移量。 >.
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

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

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

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

      [编辑] 所以这变成了我脑海中的那些分裂之一,我最终编写了一个 FilterReader,它在读取文件时保持字节偏移到字符偏移映射的缓冲区。当我们需要获取字节偏移量时,我们首先向 Woodstox 询问 char 偏移量,然后让自定义阅读器告诉我们 char 偏移量的实际字节偏移量。我们可以从元素的开头和结尾获取字节偏移量,为我们提供我们需要进入的内容,并通过将文件作为 RandomAccessFile 打开它来从文件中提取元素。

      我为此创建了一个库,位于 GitHubMaven Central。如果您只想获取重要信息,那么派对技巧就在ByteTrackingReader。 [/编辑]

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-17
        • 1970-01-01
        • 2015-10-05
        • 2014-01-16
        相关资源
        最近更新 更多