2016-04-19 1 views
2

Я обрабатываю большие файлы (с емкостью минимум 500 МБ) для разделения и слияния на C#.Какой самый быстрый способ объединить несколько файлов в файл в C#?

Мне нужно разбить файл на тысячи файлов, отсортировать эти файлы в некоторых группах и объединить их по каждой группе.

Минимальное количество файлов - 10 000.

Я реализую функцию слияния с использованием метода Stream.CopyTo(). Вот основная часть этого.

using (Stream writer = File.OpenWrite(outputFilePath)) 
{ 
     int fileNum = filePaths.Count(); 
     for (int i = 0; i < fileNum; i++) 
     { 
      using (Stream reader = File.OpenRead(filePaths.ElementAt(i))) 
      { reader.CopyTo(writer); } 
     } 
} 

Я проверил свою программу, чтобы разделить 500MB Into 17000 файлов 2 групп и объединить каждую группу 8500 файлов в один файл.

Слияние части занимает около 80 секунд. Я думаю, что это довольно медленно по сравнению с разбиением того же файла, который занимает около 15-20 секунд.

Есть ли какой-либо метод, который быстрее моего кода?

+0

Почему бы просто не использовать метод Stream.CopyTo()? Https: //msdn.microsoft.com/en-us/library/system.io.stream.copyto (v = vs.100) .aspx –

+0

Я использовал Метод Stream.CopyTo(). Но я думаю, что это медленно, в соответствии с моим результатом теста. Поэтому я запрашиваю более быстрые методы, чем метод CopyTo(). – dolgom

+0

Я написал два метода в качестве ответа, попробуйте! Удачи –

ответ

1

Ваш код выглядит хорошо, но ElementAt это код запах. Преобразуйте это в массив и вместо этого используйте [i]. Если у вас есть 10K элементов, я уверен, что вы тратите много времени.

+0

Спасибо большое !!! После его изменения требуется всего 5 секунд. – dolgom

+0

; ElementAt - такой мудак. Второй раз, когда я укусил меня, я отказался его использовать. Я не с тех пор. –

1

Возможно, попробуйте сжать файлы?

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.IO; 
using System.IO.Compression; 

class Program { 
    static void SaveCompressedFile(string filename, string data) { 
     FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write); 
     GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Compress); 
     StreamWriter writer = new StreamWriter(compressionStream); 
     writer.Write(data); 
     writer.Close(); 
    } 

    static string LoadCompressedFile(string filename) { 
     FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); 
     GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Decompress); 
     StreamReader reader = new StreamReader(compressionStream); 
     string data = reader.ReadToEnd(); 
     reader.Close(); 
     return data; 
    } 

    static void Main(string[] args) { 
     try { 
      string filename = "compressedFile.txt"; 
      string sourceString = "Source String"; 
      SaveCompressedFile(filename, sourceString); 
      FileInfo compressedFileData = new FileInfo(filename); 
      string recoveredString = LoadCompressedFile(filename); 
     } catch (IOException ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
    } 
} 

Source

Также проверьте пример сжатия каталога.

using System; 
using System.Text; 
using System.IO; 
using System.IO.Compression; 

namespace CmprDir 
{ 
    class Program 
    { 
    delegate void ProgressDelegate(string sMessage); 

    static void CompressFile(string sDir, string sRelativePath, GZipStream zipStream) 
    { 
     //Compress file name 
     char[] chars = sRelativePath.ToCharArray(); 
     zipStream.Write(BitConverter.GetBytes(chars.Length), 0, sizeof(int)); 
     foreach (char c in chars) 
     zipStream.Write(BitConverter.GetBytes(c), 0, sizeof(char)); 

     //Compress file content 
     byte[] bytes = File.ReadAllBytes(Path.Combine(sDir, sRelativePath)); 
     zipStream.Write(BitConverter.GetBytes(bytes.Length), 0, sizeof(int)); 
     zipStream.Write(bytes, 0, bytes.Length); 
    } 

    static bool DecompressFile(string sDir, GZipStream zipStream, ProgressDelegate progress) 
    { 
     //Decompress file name 
     byte[] bytes = new byte[sizeof(int)]; 
     int Readed = zipStream.Read(bytes, 0, sizeof(int)); 
     if (Readed < sizeof(int)) 
     return false; 

     int iNameLen = BitConverter.ToInt32(bytes, 0); 
     bytes = new byte[sizeof(char)]; 
     StringBuilder sb = new StringBuilder(); 
     for (int i = 0; i < iNameLen; i++) 
     { 
     zipStream.Read(bytes, 0, sizeof(char)); 
     char c = BitConverter.ToChar(bytes, 0); 
     sb.Append(c); 
     } 
     string sFileName = sb.ToString(); 
     if (progress != null) 
     progress(sFileName); 

     //Decompress file content 
     bytes = new byte[sizeof(int)]; 
     zipStream.Read(bytes, 0, sizeof(int)); 
     int iFileLen = BitConverter.ToInt32(bytes, 0); 

     bytes = new byte[iFileLen]; 
     zipStream.Read(bytes, 0, bytes.Length); 

     string sFilePath = Path.Combine(sDir, sFileName); 
     string sFinalDir = Path.GetDirectoryName(sFilePath); 
     if (!Directory.Exists(sFinalDir)) 
     Directory.CreateDirectory(sFinalDir); 

     using (FileStream outFile = new FileStream(sFilePath, FileMode.Create, FileAccess.Write, FileShare.None)) 
     outFile.Write(bytes, 0, iFileLen); 

     return true; 
    } 

    static void CompressDirectory(string sInDir, string sOutFile, ProgressDelegate progress) 
    { 
     string[] sFiles = Directory.GetFiles(sInDir, "*.*", SearchOption.AllDirectories); 
     int iDirLen = sInDir[sInDir.Length - 1] == Path.DirectorySeparatorChar ? sInDir.Length : sInDir.Length + 1; 

     using (FileStream outFile = new FileStream(sOutFile, FileMode.Create, FileAccess.Write, FileShare.None)) 
     using (GZipStream str = new GZipStream(outFile, CompressionMode.Compress)) 
     foreach (string sFilePath in sFiles) 
     { 
      string sRelativePath = sFilePath.Substring(iDirLen); 
      if (progress != null) 
      progress(sRelativePath); 
      CompressFile(sInDir, sRelativePath, str); 
     } 
    } 

    static void DecompressToDirectory(string sCompressedFile, string sDir, ProgressDelegate progress) 
    { 
     using (FileStream inFile = new FileStream(sCompressedFile, FileMode.Open, FileAccess.Read, FileShare.None)) 
     using (GZipStream zipStream = new GZipStream(inFile, CompressionMode.Decompress, true)) 
     while (DecompressFile(sDir, zipStream, progress)); 
    } 

    public static int Main(string[] argv) 
    { 
     if (argv.Length != 2) 
     { 
     Console.WriteLine("Usage: CmprDir.exe <in_dir compressed_file> | <compressed_file out_dir>"); 
     return 1; 
     } 

     string sDir; 
     string sCompressedFile; 
     bool bCompress = false; 
     try 
     { 
     if (Directory.Exists(argv[0])) 
     { 
      sDir = argv[0]; 
      sCompressedFile = argv[1]; 
      bCompress = true; 
     } 
     else 
      if (File.Exists(argv[0])) 
      { 
      sCompressedFile = argv[0]; 
      sDir = argv[1]; 
      bCompress = false; 
      } 
      else 
      { 
      Console.Error.WriteLine("Wrong arguments"); 
      return 1; 
      } 

     if (bCompress) 
      CompressDirectory(sDir, sCompressedFile, (fileName) => { Console.WriteLine("Compressing {0}...", fileName); }); 
     else 
      DecompressToDirectory(sCompressedFile, sDir, (fileName) => { Console.WriteLine("Decompressing {0}...", fileName); }); 

     return 0; 
     } 
     catch (Exception ex) 
     { 
     Console.Error.WriteLine(ex.Message); 
     return 1; 
     } 
    } 
    } 
} 

Source

+0

Могу ли я использовать эту функцию для слияния файлов? Сжатие Gzip имеет свойство линейности? – dolgom

+0

Спасибо, что ответили! – dolgom

0

Почему бы просто не использовать метод 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); 
     } 
    } 
} 

ИЛИ

Делают это в кусках:

const int chunkSize = 2 * 1024; // 2KB 
var inputFiles = new[] ; 
using (var output = File.Create("output.dat")) 
{ 
    foreach (var file in inputFiles) 
    { 
     using (var input = File.OpenRead(file)) 
     { 
      var buffer = new byte[chunkSize]; 
      int bytesRead; 
      while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) 
      { 
       output.Write(buffer, 0, bytesRead); 
      } 
     } 
    } 
} 
+0

Спасибо, что ответили! – dolgom

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