#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 — небольшой — прочитайте его один раз и сохраните результаты в памяти.
- Файл 2 — большой — читайте его по частям. Например, вы можете использовать StreamReader.ReadLine() N раз
- По возможности объединяйте данные в памяти первого файла с каждым фрагментом второго параллельно.
- Вывод — открыть/закрыть поток только один раз, запись после обработки каждого патрона.
PS: здесь нет необходимости в буферизованных потоках, потому что потоки файлов уже буферизованы. Буферизованные потоки полезны для операций ввода-вывода в сети.