Як порізати великий текстовий файл в програмі ZennoPoster

При роботі з дуже великими файлами, розмір яких може сягати 100 гігабайт і більше проблематично робити вибірку необхідних рядків. Тому появляється необхідність розкрошити цей один великий гігантський файл на багато маленьких кусочків, з якими вже можна набагато простіше працювати.

Власне що я робив, коли мені приходилось працювати з списком всіх доменних зон, коли я скачував їх з офіційного сайту (мабуть колись про це ще розкажу) – я просто використовував ліниве зчитування рядків з файла, щоб не поміщати дані з файла в оперативну пам’ять. Виглядає це приблизно так:

string path = Path.Combine(project.Directory, "big_file.txt"); // великий текстовий файл
int chunkSize = 100000000; // Кількість рядків у нових файлах
IEnumerable<string> data = File.ReadLines(path); // проводимо ліниве зчитування
int count = 2147483647; // int.MaxValue - обмеження щоб не вийти за межі int з якими працює Skip і Take
int index = 0;
while (index < count ) {
	string fileName = Path.Combine(project.Directory, string.Format("{0}_{1}.txt", index, chunkSize)); // генеруємо імя і шлях до нового файла
	var chunk= data.Skip(index).Take(chunkSize); // беремо частину
	File.WriteAllLines(fileName, chunk); // записуємо в новий файл
	index += chunkSize; // зміщуємось на новий блок рядків
}

В фрагменті коду відбувається наступне – спочатку я сформував шлях до великого файла. Я точно знаю що він існує, і знаю що в нього багато рядків. Для того, щоб не вийти за границі int – вказую обмеження в коді. Хоча, якщо кількість рядків відомо – можна вказати їх. Можна звичайно визвати метод File.ReadLines(path).Count(), проте якщо в файлі більше ніж 2147483647 рядків (ми вийшли за межі int) – то нам це значення не важливе, адже переміщатись по рядкам нам не получиться. А якщо число менше – то якщо ми його вкажемо – код вийде по успішній стрілочці з блоку проєкта в ZennoPoster. Якщо вказати його завеликим – то робота також буде виконаною, проте вийде по червоній стрілочці, ніби відбулась помилка.

Дальше в циклі формується нове ім’я частини великого файла і туди поміщаються рядки з нашого великого файла. При чому, слід відмітити, що ці рядки не поміщаються в оперативну пам’ять, тобто все працює так, ніби ми поміщаємо в оперативну пам’ять тільки один рядок. І так, при роботі з великими файлами виконання цього процесу займає час – тому прийдеться почекати…

Так от, коли ми виходимо за межі int.MaxValue – тоді на скільки я розумію потрібно використовувати спочатку порізку на фрагменти розміром int.MaxValue, а вже потім дробити їх на менші частини. Я би зробив приблизно так:

string path = Path.Combine(project.Directory, "big_file.txt"); // великий текстовий файл
string path1 = Path.Combine(project.Directory, "1.txt");// перший блок розміром int.MaxValue рядків
string path2 = Path.Combine(project.Directory, "2.txt"); // залишок - всі інші рядки
IEnumerable<string> data = File.ReadLines(path);

File.WriteAllLines(path1, data.Take(int.MaxValue)); // перший блок
File.WriteAllLines(path2, data.Skip(int.MaxValue)); // залишок

Після чого, великий файл я би видалив.
Файлу з залишком дав би ім’я великого файла.
І знову виконав би цей фрагмент коду, змінивши ім’я для файла з першим блоком.
Таким чином в мене назбиралось би великих файлів декілька.
І потім я уже різав би їх на менші частини, кодом який приводив вище.

Але якщо Ви вважаєте, що є спосіб, який це зробить швидше – пишіть в коментарях. Чесно кажучи не часто приходиться працювати з настільки великими файлами, а для одноразових порізок – цих фрагментів більш ніж достатньо. Питався також у ChatGPT, але він рекомендує використовувати
FileStream, а я колись пробував його використовувати і суттєвої різниці по часу не побачив, тому навіть приводити варіант з використанням FileStream не бачу сенсу (сенс напевне був би, якщо б мені приходилось якось модифікувати кожний рядок, хоча думаю і тут Linq повинен був би себе гарно показувати).

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *