Этот код делит файл на части. И вот вопрос: Есть один минус и сразу вопрос: Как сделать что бы если количество строк не кратно количеству частей то последний файл меньше ?. То есть пусть все первые файлы равны, последний меньше. Потому что в данном случае делит не правильно. Code: procedure TForm1.Button1Click(Sender: TObject); const //Количество частей на которые надо разбить файл. N = 10; var F1, F2 : File; i, SizePart, SizePartAdd : Cardinal; Buff : array of Byte; begin if OpenDialog1.InitialDir = '' then begin OpenDialog1.InitialDir := ExtractFilePath(Application.ExeName); end; if not OpenDialog1.Execute then Exit; if not FileExists(OpenDialog1.FileName) then begin ShowMessage('Указанный файл не найден. Действие отменено.'); Exit; end; AssignFile(F1, OpenDialog1.FileName); Reset(F1, 1); if FileSize(F1) < N then begin ShowMessage('Указанный файл слишком мал. Разбиение отменено.'); CloseFile(F1); Exit; end; SizePart := FileSize(F1) div N; SizePartAdd := FileSize(F1) mod N; SetLength(Buff, SizePart); for i := 1 to N do begin AssignFile(F2, OpenDialog1.FileName + '.part' + IntToStr(i)); Rewrite(F2, 1); BlockRead(F1, Pointer(Buff)^, SizePart); BlockWrite(F2, Pointer(Buff)^, SizePart); if (i = N) and (SizePartAdd > 0) then begin BlockRead(F1, Pointer(Buff)^, SizePartAdd); BlockWrite(F2, Pointer(Buff)^, SizePartAdd); end; CloseFile(F2); end; CloseFile(F1); end;
Нужно в этом коде исправить чтобы файл разбивался как построчно, так и по файлам. Две функции. Есть вот такой код но он медленный. Тот что выше работает с большими размерами файлов. А этот нет и постоянно ошибка -out-of-memory-delphi (В этом что ниже). Code: procedure TForm1.Button1Click(Sender: TObject); var n,k,i,j,p,q:integer; t,t1:TStringList; begin n:=strtoint(Edit1.Text);//количество разбиений t:=TStringList.Create; t.LoadFromFile('0.txt'); //если число строк кратно n if t.Count mod n=0 then k:=t.Count div n else k:=t.Count div n+1;//если не кратно, то последний файл меньше i:=0; p:=0; //номер файла while i<t.Count do begin t1:=TStringList.Create; inc(p); if t.Count-i-1<k then q:=t.Count-1 //если осталось меньше k строк else q:=i+k-1; for j:=i to q do t1.Add(t[j]); t1.SaveToFile(inttostr(p)+'.txt'); t1.Free; i:=i+k; end; t.Free; end;
Как я уже говорил в предыдущей теме, StringList это зло. Удобно конечно, но не знаешь как оно будет работать при больших объемах Code: program Project1; {$WARNINGS OFF} {$HINTS OFF} {$APPTYPE CONSOLE} uses SysUtils, windows, math; var fHandle: DWORD; fSize:DWORD; //хэндл файла, который делим BytesRead: DWORD; //кол-во записаных/считаных байт pBuf: Pointer; //буфер для чтения/записи частей partSize: integer; //размер равных частей SpecificLast:byte; //триггер неравной последней части lastPartSize: integer; //размер последней части i:integer; nfHandle: DWORD; //хэндл создаваемой части const partCount = 9; //количество частей begin //Открываем файл fHandle:= CreateFileA('./Base2.txt', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); fSize:=GetFileSize(fHandle, nil) //Получаем размер файла в байтах if fSize mod partCount <> 0 //Проверяем делится ли нацело then begin SpecificLast:=1; //Если нет, тогда делаем дописывание неравной части partSize:=Round(RoundTo(fSize/partCount, -2)); //Размер частей вычисляется через округление lastPartSize:=fSize-partSize*(partCount-1); //Размер последней части=размер файла - количество частей * размер растей end else partSize:=fSize div partCount; //Делим нацело Writeln('File size: ', fSize); Writeln('Part count: ', partCount); Writeln('Part size: ', partSize); Writeln('LastPart size: ', lastPartSize); pBuf:=VirtualAlloc(nil, partSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE); //Выделяем память под буфер for i:=1 to partCount-SpecificLast do //Кол-во итерраций в зависимости от деления нацело begin nfHandle:= CreateFileA(PChar('Part'+IntToStr(i)+'.txt'), GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); ReadFile(fHandle, pBuf^, partSize, BytesRead, nil); //Читаем из файла в буфер WriteFile(nfHandle, pBuf^, partSize, BytesRead, nil); //Пишем из буфера в файл CloseHandle(nfHandle); //Закрываем хэндл текущей части end; VirtualFree(pBuf, 0, MEM_RELEASE); //Освобождаем буфер if SpecificLast=1 then begin pBuf:=VirtualAlloc(nil, lastPartSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE); //Выделяем память под буфер для остатков nfHandle:= CreateFileA(PChar('Part'+IntToStr(partCount)+'.txt'), GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); ReadFile(fHandle, pBuf^, lastPartSize, BytesRead, nil); //И делаем все то же WriteFile(nfHandle, pBuf^, lastPartSize, BytesRead, nil); //что и в цикле выше CloseHandle(nfHandle); //только с меньшим размером VirtualFree(pBuf, 0, MEM_RELEASE); end; Writeln('Done!'); Readln; CloseHandle(fHandle); //Закрываем открытый файл со строками end. Как-то так. по-идее проблем с памятью быть не должно. Хотя если ты хочешь обрабатывать многогигобайтные файлы на компе со 128МБ оперативы, то код конечно придется переписывать p.s.: еще нужно дописать проверку возвращаемых значений, посколько это пример, я защиту от дурака не писал)
Ну например здесь: Code: fHandle:= CreateFileA('./Base2.txt', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); fHandle не должен быть равен INVALID_HANDLE_VALUE. Подробнее про функцию CreateFileA можно почитать тут: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx или тут http://vsokovikov.narod.ru/New_MSDN_API/Menage_files/fn_createfile.htm По аналогии и с остальными вызовами WinAPI