【问题标题】:Combine many XML files into single stream将许多 XML 文件合并为单个流
【发布时间】:2017-01-06 23:38:21
【问题描述】:

我有数百个一个接一个地处理的 XML 文件。我想将其他现有代码应用于文件中的数据。但是,编写该代码是为了期望单个文件或表示单个文件的流。

我看过

How do I concatenate two System.Io.Stream instances into one?

但是,Marc 的答案中提出的StreamEnumerator 要求同时打开所有相关文件的流。考虑到我的文件数量很多,这似乎不是一个好方法。

现有代码像这样使用流:

XmlReader reader = XmlReader.Create(xmlStream);

有没有更好的方法将多个文件合并到一个流中?

【问题讨论】:

  • 这个方法好像没问题。我认为您必须在流的开头添加类似“-”的内容,在添加每个文件时删除“”并添加“”在流的末尾。
  • 我试图避免同时打开数百甚至数千个流。
  • 该方法包括创建单个 xmlStream 以供现有代码使用。运行一个单独的线程通过读取一个和另一个要合并的文件来在流上写入数据。启动单独的线程后,执行“XmlReader reader = XmlReader.Create(xmlStream);”。
  • @HenkHolterman:我正在尝试先找到一个通用解决方案,然后对其进行调整以无缝结合 XML 数据。

标签: c# .net


【解决方案1】:

好吧,我会编写自己的扩展 System.IO.Stream 的类,并通过重载 CanRead 和 Read 方法按需加入这些流。像这样的东西(只是一个概念,你需要微调这段代码):

using System;
using System.Diagnostics;
using System.IO;
using System.Xml;

namespace ConsoleApplication1
{

    public class CombinedXmlStream : Stream
    {
        private Stream currentStream, startStream, endStream;
        private String[] files;
        private int currentFile = -2;
        private bool endReached = false;

        private static Stream ToStream(String str)
        {
            MemoryStream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(str);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        public CombinedXmlStream(String start, String end, params String[] files)
        {
            this.files = files;
            startStream = ToStream(start);
            endStream = ToStream(end);

        }

        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 { return 0; } set { } }

        public override void Flush() { throw new NotImplementedException(); }

        public override long Seek(long offset, 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 int Read(byte[] buffer, int offset, int count)
        {
            doSwitching();

            int output = currentStream.Read(buffer, offset, count);

            if (output == 0)
            {
                doSwitching(true);
                if (currentStream != null)
                {
                    return Read(buffer, offset, count);
                }
            }

            return output;
        }

        private void doSwitching(bool force = false)
        {
            if (force || currentStream == null || !currentStream.CanRead)
            {
                if (currentStream != null)
                {
                    currentStream.Close();
                    currentStream = null;
                }

                currentFile++;
                if (currentFile == -1)
                {
                    currentStream = startStream;
                }
                else if (currentFile >= files.Length && !endReached)
                {
                    currentStream = endStream;
                    endReached = true;
                }
                else if (!endReached)
                {
                    currentStream = new FileStream(files[currentFile], FileMode.Open);
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Debug.WriteLine("Test me");
            using (XmlReader reader = XmlReader.Create(new CombinedXmlStream("<combined>", "</combined>", @"D:\test.xml", @"D:\test2.xml")))
            {
                //reader.MoveToContent();
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        Debug.WriteLine("Node: " + reader.Name);
                    }
                }
            }
        }
    }
}

【讨论】:

  • 似乎这仅在 Read() 请求不跨越两个物理文件之间的边界时才有效。您似乎还假设每个流都调用 CanRead,这似乎不太可能。
  • 你检查了吗?每次读取后都会多次调用 CanRead(通过简单地扩展 FileStream 并覆盖这两种方法并使用断点和/或调试控制台输出来测试自己)。接下来,边界不是问题,但在这个例子中你是对的。但是话又说回来,您可以忽略 CanRead 并在 Read 方法中执行所有逻辑。原则保持不变:创建自己的流实现,在正确的时间合并文件。
  • @EricJ。使用完整的工作概念证明重新编辑了我的示例。创建两个 XML 文件(但它们不能有 序言 - 这只是 POC,因此您需要自己添加一个),这将组合这些流。不要担心文件之间的边界 - 读取被调用,直到它到达 EOF 然后涉及到切换。另请注意,此代码只是在 10 分钟左右编写的 POC :)
  • 感谢您在读取期间实现切换流的逻辑。我试试看。
猜你喜欢
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 2015-04-12
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多