将多个文件合并为单个文件

码:

static void MultipleFilesToSingleFile(string dirPath, string filePattern, string destFile) { string[] fileAry = Directory.GetFiles(dirPath, filePattern); Console.WriteLine("Total File Count : " + fileAry.Length); using (TextWriter tw = new StreamWriter(destFile, true)) { foreach (string filePath in fileAry) { using (TextReader tr = new StreamReader(filePath)) { tw.WriteLine(tr.ReadToEnd()); tr.Close(); tr.Dispose(); } Console.WriteLine("File Processed : " + filePath); } tw.Close(); tw.Dispose(); } } 

我需要优化它,因为它非常慢:平均大小为40 – 50 Mb XML文件的45个文件需要3分钟。

请注意:平均45 MB的45个文件只是一个例子,它可以是m大小的n个文件,其中n是千和m ,平均可以是128 Kb。 简而言之,它可以变化。

您能否提供有关优化的任何观点?

为什么不直接使用Stream.CopyTo()方法 ?

 private static void CombineMultipleFilesIntoSingleFile(string inputDirectoryPath, string inputFileNamePattern, string outputFilePath) { string[] inputFilePaths = Directory.GetFiles(inputDirectoryPath, inputFileNamePattern); Console.WriteLine("Number of files: {0}.", inputFilePaths.Length); using (var outputStream = File.Create(outputFilePath)) { foreach (var inputFilePath in inputFilePaths) { using (var inputStream = File.OpenRead(inputFilePath)) { // Buffer size can be passed as the second argument. inputStream.CopyTo(outputStream); } Console.WriteLine("The file {0} has been processed.", inputFilePath); } } } 

一种选择是利用复制命令,让它做得很好。

就像是:

 static void MultipleFilesToSingleFile(string dirPath, string filePattern, string destFile) { var cmd = new ProcessStartInfo("cmd.exe", String.Format("/c copy {0} {1}", filePattern, destFile)); cmd.WorkingDirectory = dirPath; cmd.UseShellExecute = false; Process.Start(cmd); } 

我会使用BlockingCollection来读取,以便您可以同时读写。
显然应该写入单独的物理磁盘以避免硬件争用。 此代码将保留顺序。
读取比写入更快,因此不需要并行读取。
再次,因为读取将更快地限制集合的大小,因此读取不会比写入更远。
在写入当前文件的同时读取单个下一个单一的简单任务有不同文件大小的问题 – 写一个小文件比读一个大文件快。

我使用此模式在T1上读取和解析文本,然后在T2上插入SQL。

 public void WriteFiles() { using (BlockingCollection bc = new BlockingCollection(10)) { // play with 10 if you have several small files then a big file // write can get ahead of read if not enough are queued TextWriter tw = new StreamWriter(@"c:\temp\alltext.text", true); // clearly you want to write to a different phyical disk // ideally write to solid state even if you move the files to regular disk when done // Spin up a Task to populate the BlockingCollection using (Task t1 = Task.Factory.StartNew(() => { string dir = @"c:\temp\"; string fileText; int minSize = 100000; // play with this StringBuilder sb = new StringBuilder(minSize); string[] fileAry = Directory.GetFiles(dir, @"*.txt"); foreach (string fi in fileAry) { Debug.WriteLine("Add " + fi); fileText = File.ReadAllText(fi); //bc.Add(fi); for testing just add filepath if (fileText.Length > minSize) { if (sb.Length > 0) { bc.Add(sb.ToString()); sb.Clear(); } bc.Add(fileText); // could be really big so don't hit sb } else { sb.Append(fileText); if (sb.Length > minSize) { bc.Add(sb.ToString()); sb.Clear(); } } } if (sb.Length > 0) { bc.Add(sb.ToString()); sb.Clear(); } bc.CompleteAdding(); })) { // Spin up a Task to consume the BlockingCollection using (Task t2 = Task.Factory.StartNew(() => { string text; try { while (true) { text = bc.Take(); Debug.WriteLine("Take " + text); tw.WriteLine(text); } } catch (InvalidOperationException) { // An InvalidOperationException means that Take() was called on a completed collection Debug.WriteLine("That's All!"); tw.Close(); tw.Dispose(); } })) Task.WaitAll(t1, t2); } } } 

BlockingCollection类

你可以做几件事:

  • 根据我的经验,默认缓冲区大小可以增加到大约120K的显着优势,我怀疑在所有流上设置一个大缓冲区将是最简单和最引人注目的性能助推器:

     new System.IO.FileStream("File.txt", System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read, 150000); 
  • 使用Stream类,而不是StreamReader类。

  • 将内容读入大缓冲区,立即将它们转储到输出流中 – 这将加速小文件操作。
  • 不需要冗余关闭/处置:你有using声明。