Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.RU > Delphi: Общие вопросы > Разбить файл на указанное количество строк \ Доработка |
Автор: Kirilis2018 11.11.18, 08:11 |
Ребята, выручите в решении данного вопроса. Задание: Есть текстовый файл, в котором 150000 строк. Указываю число 15000 (на сколько строк разбить) и программа разбивает его на файлы, в каждом из которых 15000 строк. Получаю результат в виде 10 файлов по 15000 строк в каждом. Если остается остаток меньше 15000 строк, то его записываем в последний файл. Код что ниже - работает. Но меня смущает вот этот участок кода (Тут остаточная часть, текстового файла - грузится в память и жестко -забивает её. Как можно переделать построчно ?): <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> // Если осталось прочитано строк меньше, чем значение Amount, то // эти строки будут содержаться в OutputFile. Все, что останется - загрузить // предыдущий файл и добавить к нему эти строки. В связи с этим приходиться // создавать дополнительный список. TempList := TStringList.Create; try TempList.LoadFromFile(OutputFileName + IntToStr(Index - 1) + FileExt); TempList.AddStrings(OutputFile); TempList.SaveToFile(OutputFileName + IntToStr(Index - 1) + FileExt); finally TempList.Free; Вот основной код в котором есть участок кода что выше: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> procedure TForm1.aButton1Click(Sender: TObject); var FileToRead: TextFile; TempList: TStringList; OutputFile: TStringList; ReadString: String; OutputFileName: String; FileExt: String; Amount: Integer; CurrentStringIndex: Integer; LineIndex: Integer; Index: Integer; Delta: Integer; i: Integer; begin Amount := 6; OutputFileName := 'C:\FileName_'; FileExt := '.txt'; // Открываем файл для чтения AssignFile(FileToRead, 'C:\FileToRead.txt'); try Reset(FileToRead); OutputFile := TStringlist.Create; try CurrentStringIndex := 0; LineIndex := 0; Index := 0; // Работаем со строками в файле, открытом для чтения while not EoF(FileToRead) do begin if LineIndex <> Amount then begin // Читаем строку из файла ReadLn(FileToRead, ReadString); // Добавляем строку в список OutputFile.Add(ReadString); Inc(CurrentStringIndex); Inc(LineIndex); end else // Прочитали нужное количество строк - сохраним их! begin LineIndex := 0; OutputFile.SaveToFile(OutputFileName + IntToStr(Index) + FileExt); OutputFile.Clear; // Увеличиваем индекс файла Inc(Index); end; end; // Если осталось прочитано строк меньше, чем значение Amount, то // эти строки будут содержаться в OutputFile. Все, что останется - загрузить // предыдущий файл и добавить к нему эти строки. В связи с этим прходится // создавать дополнительный список. TempList := TStringList.Create; try TempList.LoadFromFile(OutputFileName + IntToStr(Index - 1) + FileExt); TempList.AddStrings(OutputFile); TempList.SaveToFile(OutputFileName + IntToStr(Index - 1) + FileExt); finally TempList.Free; end; finally OutputFile.Free; end; finally // Закрываем файл CloseFile(FileToRead); end; end; |
Автор: Славян 11.11.18, 09:26 |
Я б построчно делал, всё равно всё прокэшируется и существенного отличия не увижу: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> for i:=0 to N-1 do begin if (i mod K)=0 then поменятьФайлВыхода; // сравнение на каждой строке ничтожно по времени в сравнении с чтением строки (ниже) ReadLn( входной ... ) WriteLn( выходной ...); Inc(i); end; Добавлено Inc, небось, не нужен, сие на автомате в Паскале (не помню). |
Автор: Kirilis2018 11.11.18, 09:39 |
Славян, Идея, но к примеру если делить по 11 000 000 строк то даже в построчном чтении выскакивает ошибка нехватки памяти... Почему так ? |
Автор: Славян 11.11.18, 10:27 |
Не, ничего не выскакивает! Надо просто эту же строку и записывать, а не класть в список, кой потом хочется махом сбросить в файл. Сразу делайте (запись прочитаной строчки) и всё будет норм. |
Автор: Kirilis2018 11.11.18, 10:30 |
Славян, Ух и ох , помогите исправить. Чего то я сегодня вообще туплю и конкретно. |
Автор: Славян 11.11.18, 10:32 |
Цитата Kirilis2018 @ Именно такое быть может, думается, токмо если в файле всего несколько строк (а задали "делить по 11 млн"), но каждая строка - в несколько гигабайт. Ну тогда - да, надо будет тщательнее строку порциями читать/записывать. Но тут явно не этот (весьма экзотический) случай. если делить по 11 000 000 строк то даже в построчном чтении выскакивает ошибка нехватки памяти... Добавлено Да легко. Бросьте код с построчным чтением/записью, - посмотрим что и как там осталось дошлифовать. |
Автор: Kirilis2018 11.11.18, 10:36 |
Славян, тут файл в 1 гигабайт и его нужно разделить на 11 000 000 строк. Строки не длинные, по 120 символов строка. И остаток, если меньше 11 000 000 записать в отдельный файл. Вот потому не знаю как правильно оптимизировать код и прошу потому помощи! Добавлено Славян, Вот мой код, посмотрите: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> procedure Tfrm_Main.Button1Click(Sender: TObject); var FileToRead: TextFile; TempList: TStringList; OutputFile: TStringList; ReadString: String; OutputFileName: String; FileExt,_FilePath: String; Amount: Integer; CurrentStringIndex: Integer; LineIndex: Integer; p: Integer; Index: Integer; Delta: Integer; i: Integer; begin if opendialog6.execute then Amount := 11000000; OutputFileName := 'File'; FileExt := '.txt'; // Открываем файл для чтения AssignFile(FileToRead, opendialog6.filename); try Reset(FileToRead); OutputFile := TStringlist.Create; try CurrentStringIndex := 0; LineIndex := 0; Index := 1; // Работаем со строками в файле, открытом для чтения while not EoF(FileToRead) do begin if LineIndex <> Amount then begin // Читаем строку из файла ReadLn(FileToRead, ReadString); // Добавляем строку в список OutputFile.Add(ReadString); Inc(CurrentStringIndex); Inc(LineIndex); end else // Прочитали нужное количество строк - сохраним их! begin LineIndex := 0; CreateDir('f' + inttostr(Index)); OutputFile.SaveToFile('f' + inttostr(Index)+'\' + OutputFileName + IntToStr(Index)+ FileExt); OutputFile.Clear; // Увеличиваем индекс файла Inc(Index); end; end; // Сохраняем остаток TempList := TStringList.Create; try TempList.AddStrings(OutputFile); TempList.SaveToFile('f' +IntToStr(Index) + FileExt); finally TempList.Free; end; finally OutputFile.Free; end; finally // Закрываем файл CloseFile(FileToRead); end; end; |
Автор: Славян 11.11.18, 10:50 |
Так всё ж написано же! Ваяйте нечто вида: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> LineIndex := 0; // Работаем со строками в файле, открытом для чтения while not EoF(FileToRead) do begin if LineIndex mod Amount = 0 then begin CloseFile(FileToWrite); // Закрываем предыдущий файл ReadString := OutputFileName + IntToStr(Index) + FileExt // выходной (тут бы ведущие нули) AssignFile(FileToWrite, ReadString); // как-то для записи. Не помню как в Паскале end; ReadLn(FileToRead, ReadString); // Читаем строку из файла WriteLn(FileToWrite, ReadString); // Пишем строку в файл Inc(LineIndex); // ещё одну строку обработали! end; ... CloseFile(FileToWrite); // Закрываем хвост |
Автор: Kirilis2018 11.11.18, 11:00 |
Славян, Туплю немного я еще, ну попробую разобраться. И на этом спасибо. |
Автор: Славян 11.11.18, 11:27 |
В итоге так заработало: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Имена входных/выходных файлов поправьте на свои. FileName := 'D:\1\22'; Amount := 4; Index := 0; LineIndex := 0; // Работаем со строками в файле, открытом для чтения // Открываем файл для чтения AssignFile(FileToRead, FileName); Reset( FileToRead ); OutputFileName := 'd:\1\File'; FileExt := '.txt'; ReadString := OutputFileName + IntToStr(Index) + FileExt; // выходной (тут бы ведущие нули) AssignFile( FileToWrite, ReadString); // как-то для записи. Не помню как в Паскале ReWrite( FileToWrite ); while not EoF(FileToRead) do begin if (LineIndex mod Amount) = 0 then begin CloseFile(FileToWrite); // Закрываем предыдущий файл ReadString := OutputFileName + IntToStr(Index) + FileExt; // выходной (тут бы ведущие нули) AssignFile(FileToWrite, ReadString); // как-то для записи. Не помню как в Паскале ReWrite( FileToWrite ); // вот как в Паскале Inc(Index) // ещё один файл начали! end; ReadLn(FileToRead, ReadString); // Читаем строку из файла WriteLn(FileToWrite, ReadString); // Пишем строку в файл Inc(LineIndex) // ещё одну строку обработали! end; // ... CloseFile(FileToRead); // Закрываем CloseFile(FileToWrite); // Закрываем хвост |
Автор: Kirilis2018 11.11.18, 11:40 |
Славян, 1000 благодарностей и миллионы плюсов - Вам!. Все заработало как нужно!. Я 4 часа долбил код, по своей неопытности. А у Вас мгновенно все получилось. Низкий поклон Вам! |
Автор: Славян 11.11.18, 12:23 |
Эх, хорошо, если бы вы проверили 2 способа: построчный и через списки строк, да сказали нам отличия по времени. А то у меня вроде нет таких могучих текстовых файлов. Хотя... есть один XML на 15 гиг, но... как-то неохота возиться, если честно. |
Автор: Kirilis2018 11.11.18, 14:36 |
Славян, Проверил, на файле в 8 гигабайт. Построчно, у меня, обрабатывает за 30 минут 41 секунду , и через списки строк за 24 минуты 25 секунд. Но построчно тянет большие куски а вот в варианте по списку, крошиться оперативная память, даже на 500 мегабайтах. Потому, преимущественно - использовать вариант построчно, как для меня. У кого, правда, оперативы туча, то можно и по списку - но по скорости, этот вариант, особо не выигрывает. Это конечно мое мнение, я не сильно опытный в программировании, можно сказать начинающий. |
Автор: Славян 11.11.18, 16:34 |
Цитата Kirilis2018 @ Ясно. Спасибо!! Надо будет самому́ что-то в этом варианте покопать - интересно стало! построчно тянет большие куски а вот в варианте по списку, крошиться оперативная память, даже на 500 мегабайтах. |
Автор: Kirilis2018 11.11.18, 16:40 |
Славян, Да это Вам спасибо. Я просто разными документами занимаюсь. И мне вот программы по тексту нужны. Но то что продают просто жесть... Потому пытаюсь сам под свои нужды сделать. Еще раз спасибо! |