Лучший подход для декартова произведения или 2 больших текстовых файлов

#c# #.net #windows #filestream #large-files

Вопрос:

У меня проблема, когда я хочу объединить 2 больших текстовых файла вместе и создать новый файл с декартовым произведением 2 входных файлов. Я знаю, как будет выглядеть код, но не уверен, на каком языке создать такую утилиту. У меня есть Windows server, и я знаком с C#, сценарием оболочки.

Примечание : Файл1 может быть размером около 20 МБ, а Файл2 может содержать около 6000 записей. Итак, чего я хочу добиться, так это скопировать 20 МБ данных 6000 раз в новый файл.

Ниже приведены более мелкие примеры того, как будут выглядеть мои файлы

Файл1

 Head-A-AA-AAA Child-A1-AA1-AAA1 Child-A2-AA2-AAA2 Child-A3-AA3-AAA3 Head-B-BB-BBB Child-B1-BB1-BBB1 Child-B2-BB2-BBB2 Child-B3-BB3-BBB3  

Файл2

 Store1 Store2 Store3  

Ожидаемый выходной файл

 Store1 Head-A-AA-AAA Child-A1-AA1-AAA1 Child-A2-AA2-AAA2 Child-A3-AA3-AAA3 Head-B-BB-BBB Child-B1-BB1-BBB1 Child-B2-BB2-BBB2 Child-B3-BB3-BBB3 Store2 Head-A-AA-AAA Child-A1-AA1-AAA1 Child-A2-AA2-AAA2 Child-A3-AA3-AAA3 Head-B-BB-BBB Child-B1-BB1-BBB1 Child-B2-BB2-BBB2 Child-B3-BB3-BBB3 Store3 Head-A-AA-AAA Child-A1-AA1-AAA1 Child-A2-AA2-AAA2 Child-A3-AA3-AAA3 Head-B-BB-BBB Child-B1-BB1-BBB1 Child-B2-BB2-BBB2 Child-B3-BB3-BBB3  

Ищете предложения, будет ли код C# со службой Windows служить какой-либо цели или мне нужно использовать любой другой инструмент/утилиту/сценарии?

ИЗМЕНИТЬ : Создано под кодом c#. Но для создания выходного файла объемом 150 ГБ требуется несколько часов. Я ищу более быстрый способ. Я беру содержимое из файла 1 и копирую его для каждой записи во втором файле

 FileInfo[] fi;  Listlt;FileInfogt; TodaysFiles = new Listlt;FileInfogt;();  string PublishId;  DirectoryInfo di = new DirectoryInfo(@"\InputPath");   fi = di.GetFiles().Where(file =gt; file.FullName.Contains("TRANSMIT_MASS")).ToArray();   foreach (FileInfo f in fi)  {  string[] tokens = f.Name.Split('_');  if(tokens[2] == DateTime.Now.AddDays(1).ToString("MMddyyyy"))  {  PublishId = tokens[0];  string MACSFile = @"\OutputPath\"   PublishId   ".txt";  string path =f.FullName;   string StoreFile = di.GetFiles().Where(file =gt; file.Name.StartsWith(PublishId) amp;amp; file.Name.Contains("SUBS")).Single().FullName;   using (FileStream fs = File.Open(StoreFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))  using (BufferedStream bs = new BufferedStream(fs))  using (StreamReader sr = new StreamReader(bs))  {  using (StreamWriter outfile = new StreamWriter(MACSFile))  {  String StoreNumber;  while ((StoreNumber = sr.ReadLine()) != null)  {  Console.WriteLine(StoreNumber);  if (StoreNumber.Length gt; 5)  {  using (FileStream fsProfile = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))  using (BufferedStream bsProfile = new BufferedStream(fsProfile))  using (StreamReader srProfile = new StreamReader(bsProfile))  {  outfile.WriteLine(srProfile.ReadToEnd().TrimEnd());    }   }   }  }  }   }  }  

Комментарии:

1. Любой язык может это сделать, в чем именно проблема? Считывайте из одного файла, записывайте в другой.

2. @Evk : Проблема в том, какой язык или ОС смогут справиться с таким большим размером. Размер выходного файла может составлять около 150 ГБ.

3. Язык и операционная система не имеют значения, пока у вас есть свободные 150 ГБ на вашем диске. Вы не будете создавать этот файл в памяти — вы сразу запишете его в файл.

4. @Evk: Я думал о том же, но для создания выходного файла объемом 150 ГБ требуется несколько часов. Добавил свой код C# в вопрос.

5. Вы проверили, поддерживает ли ваше оборудование ваши требования (копирование 150 гигабайт за 1 час или меньше)? Рассматривали ли вы возможность чтения с другой шины (а не только с другого диска), на которую вы пишете? И 2.: Вы пробовали сохранить 20 МБ, которые вы копируете снова и снова, полностью в памяти?

Ответ №1:

Вы упомянули сценарий оболочки. Вот пример рабочей оболочки:

 while read line; do  echo "$line" gt;gt; Output  cat File1 gt;gt; Output done lt; File2  

Здесь строки из File2 зацикливаются и записываются вместе со всем File1 в произвольный выходной файл Output .

Легко запустить, сохранив его в локальном файле something.sh и запустив sh something.sh .

Комментарии:

1. Есть ли шансы, что это позволит сгенерировать файл объемом 150 ГБ в течение часа?

Ответ №2:

Мы могли бы дополнительно оптимизировать код для повышения производительности за счет памяти. Все это переработайте, чтобы сделать его чище.

Файл1 : 6000 строк

Файл2 : 20 МБ

Поскольку файл 1 (файл меньшего размера) содержит всего несколько строк, он будет считывать весь файл в память и зацикливаться на нем.

 foreach (string line in File.ReadAllLines(File1))  

Если у вас все еще есть объем памяти, вы также можете прочитать весь второй файл в память

 var file2 = File.ReadAllText(File2)  

Теперь все, что вам нужно сделать, это добавить все в 3-й файл. Которые мы не будем хранить в памяти из-за размера.

Таким образом, весь код будет

 var file2 = File.ReadAllText(File2); var destinationFile = "destination/file/path";  foreach (string line in File.ReadAllLines(File1)){ File.AppendAllText(destinationFile, line); File.AppendAllText(destinationFile, file2); }  

Дальнейшая оптимизация: Пропущено, чтобы код был простым

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

Для дальнейшей оптимизации вы можете использовать StringBuilder, загрузив в него файл2.

 var file2 = new StringBuilder(File.ReadAllText(File2));  

И мутировать его. Это должно предотвратить 2 вызова файла.Добавьте текст и повысьте производительность.

Комментарии:

1. Читайте, этот вопрос решен, в комментариях. Локальный скрипт запускается менее чем за 5 минут, оставляя мой ответ для дальнейшей оптимизации.

Ответ №3:

Трудно сократить время ввода-вывода. Вы можете попробовать случай с чтением/записью большими порциями (я думаю, что это более эффективно, потому что операции ввода-вывода требуют выделения/высвобождения ресурсов ОС). Поэтому, если вы прочитаете все, соберете результат в памяти, запишете в файл, то он потратит меньше времени на ввод-вывод. Более высокая скорость здесь достигается за счет операций в памяти, поскольку операции с оперативной памятью и процессором обрабатываются очень быстро по сравнению с операциями ввода-вывода.

  1. Файл 1 — небольшой — прочитайте его один раз и сохраните результаты в памяти.
  2. Файл 2 — большой — читайте его по частям. Например, вы можете использовать StreamReader.ReadLine() N раз
  3. По возможности объединяйте данные в памяти первого файла с каждым фрагментом второго параллельно.
  4. Вывод — открыть/закрыть поток только один раз, запись после обработки каждого патрона.

PS: здесь нет необходимости в буферизованных потоках, потому что потоки файлов уже буферизованы. Буферизованные потоки полезны для операций ввода-вывода в сети.