2017-01-06 5 views
0

У меня есть много сотен XML-файлов, которые обрабатываются один за другим. Я хочу применить другой, существующий код к данным в файлах. Однако этот код записывается в ожидании одного файла или потока, представляющего один файл.Объединить много файлов XML в один поток

Я посмотрел на

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

Однако StreamEnumerator представил в ответ Марка, требует потоков, которые будут открыты для всех файлов, о которых идет речь сразу. Это не похоже на хороший подход, учитывая большое количество файлов в моем случае.

Существующий код потребляет поток, как это:

XmlReader reader = XmlReader.Create(xmlStream); 

Есть ли лучший способ объединить множество файлов в один поток?

+0

Метод кажется ОК. Я думаю, что вам нужно будет добавить в начале потока что-то вроде « - », удалите «» при добавлении каждого файла и добавьте «<\global_doc>» в конец потока. – Graffito

+0

Я стараюсь не открывать сразу сотни или, возможно, несколько тысяч потоков. –

+0

Метод состоит в создании единственного xmlStream, который будет использоваться существующим кодом. Запустите отдельный поток, чтобы записать данные в поток, прочитав один файл другого, чтобы файлы были объединены. После запуска отдельного потока выполните «XmlReader reader = XmlReader.Create (xmlStream);». – Graffito

ответ

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); 
        } 
       } 
      } 
     } 
    } 
} 
+0

Кажется, это будет работать только в том случае, если запросы Read() не пересекают границу между двумя физическими файлами. Также кажется, что вы принимаете один вызов CanRead для каждого потока, что кажется маловероятным. –

+0

Вы даже проверили? CanRead вызывается несколько раз после каждого чтения (проверяйте сами, просто вытаскивая FileStream и переопределяя эти два метода и используя контрольные точки и/или отладочную консоль вывода). Далее, граница не проблема, но да в этом примере вы правы. Но опять же вы можете игнорировать CanRead и выполнять всю логику внутри метода Read. Принцип остается прежним: создайте собственную реализацию потока, которая делает слияние файлов в нужное время. – Vir

+0

@EricJ. Повторил мой пример с полным доказательством работы. Создайте два файла XML (но они не могут содержать преамбулу - это просто POC, так что вам нужно добавить себя), и это объединит эти потоки. Не беспокойтесь о границе между файлами - Read вызывается до тех пор, пока не достигнет EOF, а затем включится. Также обратите внимание, что этот код является только POC-файлом за 10 минут или около того :) – Vir

Смежные вопросы