【问题标题】:XmlReader to read from fixed length bufferXmlReader 从固定长度缓冲区读取
【发布时间】:2011-06-12 20:15:26
【问题描述】:

传入的流进入一个固定的 1024 字节缓冲区,流本身是一个拥抱 XML 文件,可能需要几轮读取才能完成。我的目标是读取缓冲区并计算出一个元素在大 XML 文件中出现了多少次。

我的挑战是,因为它实际上是一个固定长度的缓冲区,所以它不能保证 XML 的良好格式,如果我将流包装在 XmlTextReader 中,我总是会遇到异常并且无法完成读取。例如,元素可以是 abcdef,而第一个缓冲区可以以 abc 结尾,而第二个缓冲区以 def 开头。我对此感到非常沮丧,任何人都可以建议一种更好的方法来使用流媒体方式来归档它? (我不想将整个内容加载到内存中)

非常感谢

【问题讨论】:

  • XmlTextReader 应该是解决方案,只需让它管理缓冲区而不是手动进行。

标签: c# xml


【解决方案1】:

您的 1024 字节缓冲区是否来自 System.IO.Stream 的标准具体实现之一?如果是,您可以围绕基本流创建 XmlTextReader:

XmlTextReader tr = XmlTextReader.Create( myStreamInstance ) ;

如果不是——例如,你正在从某种 API “读取”缓冲区——你需要实现自己的具体 Stream,沿着这些思路(你需要做的就是充实ReadNextFrame() 方法并可能实现您的构造函数):

public class MyStream : System.IO.Stream
{
    public override bool CanRead  { get { return true  ; } }
    public override bool CanSeek  { get { return false ; } }
    public override bool CanWrite { get { return false ; } }
    public override long Length   { get { throw new NotImplementedException(); } }
    public override long Position {
                                    get { throw new NotImplementedException(); }
                                    set { throw new NotImplementedException(); }
                                  }

    public override int Read( byte[] buffer , int offset , int count )
    {
        int bytesRead = 0 ;

        if ( !initialized )
        {
            Initialize() ;
        }

        for ( int bytesRemaining = count ; !atEOF && bytesRemaining > 0 ; )
        {

            int frameRemaining = frameLength - frameOffset ;
            int chunkSize      = ( bytesRemaining > frameRemaining ? frameRemaining : bytesRemaining ) ;

            Array.Copy( frame , offset , frame , frameOffset , chunkSize ) ;

            bytesRemaining -= chunkSize ;
            offset         += chunkSize ;
            bytesRead      += chunkSize ;

            // read next frame if necessary
            if ( frameOffset >= frameLength )
            {
                ReadNextFrame() ;
            }

        }

        return bytesRead ;
    }

    public override long Seek( long offset , System.IO.SeekOrigin origin ) { throw new NotImplementedException(); }
    public override void SetLength( long value )                           { throw new NotImplementedException(); }
    public override void Write( byte[] buffer , int offset , int count )   { throw new NotImplementedException(); }
    public override void Flush()                                           { throw new NotImplementedException(); }

    private byte[] frame       = null  ;
    private int    frameLength = 0     ;
    private int    frameOffset = 0     ;
    private bool   atEOF       = false ;
    private bool   initialized = false ;

    private void Initialize()
    {
        if ( initialized ) throw new InvalidOperationException() ;

        frame       = new byte[1024] ;
        frameLength = 0 ;
        frameOffset = 0 ;
        atEOF       = false ;
        initialized = true ;

        ReadNextFrame() ;

        return ;
    }

    private void ReadNextFrame()
    {

        //TODO: read the next (or first 1024-byte buffer
        //TODO: set the frame length to the number of bytes actually returned (might be less than 1024 on the last read, right?
        //TODO: set the frame offset to 0
        //TODO: set the atEOF flag if we've exhausted the data source ;

        return ;

    }

}

然后像上面一样实例化你的 XmlReader:

System.IO.Stream     s  = new MyStream() ;
System.Xml.XmlReader xr = XmlTextReader.Create( s ) ;

干杯!

【讨论】:

    【解决方案2】:

    这是一个奇怪的目标......通常它更像是“计算元素但不将整个 XML 加载到内存”,这是微不足道的 - 编写 Stream 派生类,将您的缓冲区表示为仅转发流(类似于 NetworkStream)和正常使用 XmlReader 读取 XML(即使用 LINQ),但不构造 XmlDocument。

    如果您明确了自己的目标,其他人可能会更容易提出建议。

    【讨论】:

    • 感谢亚历克斯的回复。这种情况是在 BizTalk 中的管道组件的事件处理程序中编写代码。在运行时,BizTalk 引擎将读取下游数据(以 xml 格式),进而引发事件并向事件处理程序提供固定长度的缓冲区。我的目标是以某种方式消耗这个缓冲区来计算某些元素。但是这样做的挑战是没有保证缓冲区内的内容,因为部分非格式良好的 xml 可以在其中。
    • 但是如果在内存中缓冲整个文档是一种选择,那么您处理文档的工作将大大简化
    • 这不是一个选项。考虑每条消息缓冲数百兆字节是对硬件的犯罪
    • 好的,只要确保你已经想通了这个问题。我的经验是,当你处理这样的大消息时,I/O 往往会主导整体性能......通常情况下,如果你有足够的内存,缓冲实际上比分段访问更快(这可能不是你的情况需要什么)。
    • 查看 Nicholas Carey 对 Stream 实现的回答。满足您从不将整个内容加载到内存中的要求,并允许使用标准 XmlReader。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多