Форум: "Основная";
Текущий архив: 2005.10.09;
Скачать: [xml.tar.bz2];
ВнизПострочное чтени большого текстового файла Найти похожие ветки
← →
IceBeerg © (2005-09-13 18:26) [0]Подскажите выход из ситуации... Есть большой log файл (более 40 гигов), пытаюсь загружать его в StringList - читается не весь... Попробовал использовать FileStream, но не знаю как через него читать файл построчно... Цель - читать файл построчно, определять дату находящуюся в начале каждой строки и по этой дате выводить данные log"а. Строки файла НЕ одинаковой длинны...
← →
Изя Губерман (2005-09-13 18:37) [1]
while not EoF(f) do begin
ReadLn(f, s);
end;
← →
Digitman © (2005-09-13 18:38) [2]
> Есть большой log файл (более 40 гигов), пытаюсь загружать
> его в StringList - читается не весь
еще бы !
ведь ВАП Win32-процесса ограничен 4-мя Гб, из которых прикл.задаче доступно и того меньше.
> Попробовал использовать FileStream, но не знаю как через
> него читать файл построчно
ну, положим, FileStream явно не предназначен для этого ..
а Assign() + ReadLn() не приходило в голову использовать для оной цели ?
ведь ReadLn() как раз для построчного чтения и предназначен ..
← →
isasa © (2005-09-13 18:40) [3]Все равно весь файл :) не влезет читай блоками.
String types
Type Maximum length Memory required Used for
ShortString 255 characters 2 to 256 bytes backward compatibility
AnsiString ~2^31 characters 4 bytes to 2GB 8-bit (ANSI) characters, DBCS ANSI, MBCS ANSI, etc.
WideString ~2^30 characters 4 bytes to 2GB Unicode characters; multi-user servers and multi-language applications
← →
PVOzerski © (2005-09-13 18:42) [4]Дык выбор-то невелик. Считываем в строку блок некой "разумной" длины, находим знак(и) перехода на следующую строку. Если не нашли - доращиваем строку следующим блоком. А нашли - строку обрубаем, а по файлу - seek назад, на вычисляемую позицию начала следующей строки... Если постараться, можно усовершенствовать алгоритм так, что при наличии нескольких строк в блоки вытаскивать их всех.
Замечу, что если речь идет действительно о десятках гигабайт (а не мегабайт), придется работать через потоки или WinAPI. А то blockread работает со знаковыми 4-байтными целыми применительно к размерам и индексации блоков - на такую позицию будет не перепрыгнуть :(
← →
Котик Бегемотик (2005-09-13 18:45) [5]А может уважаемый еще и соизволит обьяснить смысл создания текстового файла такого большого размера ...
40 гигов !!! представляю себе скорость работы с ним :(
← →
isasa © (2005-09-13 18:48) [6]PVOzerski © (13.09.05 18:42) [4]
В этом плане, можно попробовать
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
DWORD: Longword; //0..4294967295 unsigned 32-bit
Будет быстрее, чем ReadLn(...)
← →
IceBeerg © (2005-09-13 18:50) [7]Котик Бегемотик (13.09.05 18:45) [5]
Это лог 1С за два года!
Изя Губерман (13.09.05 18:37) [1]
Это первое, что пришло в голову, но медоенно работает...
PVOzerski © (13.09.05 18:42) [4]
придется работать через потоки или WinAPI
наведите пожалуйста на район "раскопок" информации по WinAPI, что-то в голову ничего не лезет...
← →
Игорь Шевченко © (2005-09-13 18:52) [8]Readln для этого придумана.
Интересно, какой очередной Архангельский рекомендует читать файл путем использования StringList, поубивал бы.
IceBeerg © (13.09.05 18:50) [7]
> Это первое, что пришло в голову, но медоенно работает...
Быстрее все равно не сделаешь
← →
IceBeerg © (2005-09-13 18:53) [9]isasa © (13.09.05 18:48) [6]
в даном моменте то же прийдется использовать метод предложенный PVOzerski по поводу блочного чтения...
← →
IceBeerg © (2005-09-13 18:55) [10]Игорь Шевченко © (13.09.05 18:52) [8]
> Readln для этого придумана
это еще с Паскаля понятно
> Интересно, какой очередной Архангельский рекомендует
> читать файл путем использования StringList, поубивал
> бы.
никакой, сам лажанулся
> Быстрее все равно не сделаешь
а все же хотелось бы, но видимо никак
← →
Игорь Шевченко © (2005-09-13 18:59) [11]IceBeerg © (13.09.05 18:55) [10]
>
> никакой, сам лажанулся
Да если бы...Не ты первый в этом форуме :)
> а все же хотелось бы, но видимо никак
А главное - зачем ? Если лог копился два года, то подождать какое-то раумное время всяко можно ?
> в даном моменте то же прийдется использовать метод предложенный
> PVOzerski по поводу блочного чтения...
И зачем изобретать readln, которая делает абсолютно все то же самое ? :)
← →
PVOzerski © (2005-09-13 18:59) [12]ReadFile, SetFilePointer. Только проблема поиска знаков перевода каретки остается. На самом деле, TFileStream использует внутри себя эти же функции.
← →
PVOzerski © (2005-09-13 19:03) [13]2Игорь Шевченко:
Я не знаю формат лога 1С - может, там не #13#10 :)
← →
Anatoly Podgoretsky © (2005-09-13 19:28) [14]PVOzerski © (13.09.05 19:03) [13]
Ничего страшного, начиная с Дельфи 6 ReadLn более благосклонен к разделителям строк, по крайней мере Юникс формат понимает, насчет Макинтош неуверен, и еще более не уверен насчет извращеного хакерского формата. LFCR а учитывая как написан 1С там может быть и такое и даже все четыре варианта одновременно.
← →
Anatoly Podgoretsky © (2005-09-13 19:28) [15]PVOzerski © (13.09.05 18:59) [12]
Проблема поиска решается буферизацией, как это сделано в ReadLn
← →
Anatoly Podgoretsky © (2005-09-13 19:30) [16]Еще один хороший путь, это загнать в промышленную базу данных или напрямую, если регулярная структура или через тот же самый ReadLn
← →
Fay © (2005-09-13 23:38) [17]2 Игорь Шевченко © (13.09.05 18:52) [8]
>> Быстрее все равно не сделаешь
Спорим? 8)
← →
Defunct © (2005-09-14 01:49) [18]> IceBeerg
TLogReader = class
private
fFileName : String;
fCanRead : boolean;
fLastString : String;
fStream : TFileStream;
fStrings : TStrings;
fEof : boolean;
fErrors : integer; // Ошибочных строк
fStartPos : Int64; // Позиция начала загрузки
fLastPos : Int64; // Последняя прочитанная позиция
fNextPos : Int64; // Позиция для чтения следующей пачки
fBatchPos : Integer; // Позиция внутри порции прочитанных строк
fLastLoadedString: String;
fDontCheckMask : boolean;
fMask : String;
fMemo : TMemo;
fReverseLookup : boolean;
fSearching : boolean;
fPosFromEnd : Int64;
procedure BatchReadFromEnd;
procedure BatchRead; // читает порцию из лог файла в fStrings
procedure SetFileName(const Value: String);
function ReadString: String;
procedure SetLastLoadedString(const Value: String);
function GetEndPos: Int64;
function GetPercentDone: Integer;
procedure SetMemo(const Value: TMemo);
procedure Report(const S: String );
procedure SetReverse(const Value: boolean);
public
property FileName: String read fFileName write SetFileName;
property LastLoadedString:String read fLastLoadedString
write SetLastLoadedString; // Для инициализации продолжения
property CanRead: boolean read fCanRead;
property LastString: String read fLastString;
property Eof:boolean read fEof;
property Strings:TStrings read fStrings;
property StartPosition:Int64 read fStartPos;
property EndPosition:Int64 read GetEndPos;
property Progress:Integer read GetPercentDone;
property ReportBox: TMemo read fMemo write SetMemo;
property ReverseLookup : boolean read fReverseLookup write SetReverse;
constructor Create;dynamic;
destructor Destroy;override;
end;
{ TLogReader }
constructor TLogReader.Create;
begin
fFileName := "";
fMemo := nil;
fLastString := "";
fLastLoadedString := "";
fCanRead := false;
fStream := nil;
fEof := True;
fDontCheckMask := false;
fStrings := TStringList.Create;
end;
procedure TLogReader.Report(const S: String);
begin
if Assigned( fMemo ) then
fMemo.Lines.Add( S );
end;
procedure TLogReader.SetMemo(const Value: TMemo);
begin
fMemo := Value;
fMemo.Lines.Clear;
end;
procedure TLogReader.SetReverse(const Value: boolean);
begin
fReverseLookup := Value;
if fReverseLookup then
Report("setting lookup reverse=TRUE (searching from END of stream)")
else
Report("setting lookup reverse=FALSE (searching from BEGIN of stream");
end;
procedure TLogReader.SetFileName(const Value: String);
begin
Report("setting log file name = " + Value );
fCanRead := FileExists( Value );
if Assigned(fStream) then
FreeAndNil( fStream );
if CanRead then
begin
fFileName := Value;
fStream := TFileStream.Create( fFileName, fmOpenRead or fmShareDenyNone );
fNextPos := 0;
fStartPos := 0;
fLastPos := 0;
fStrings.Clear;
fEof := fStream.Size = 0;
fPosFromEnd := fStream.Size;
BatchRead;
Report("Assigned filename has open successfully");
end
else
begin
fFileName := "";
Report("Assigned filename not found, reseting filename");
end;
end;
procedure TLogReader.BatchReadFromEnd;
var
S : String;
i : integer;
function GetBatchPos:Int64;
begin
Result := fPosFromEnd - lrBatchSize;
if Result < 0 then
Result := 0;
end;
begin
Report("Trying to do batch read (direction=right to left)");
fNextPos := fPosFromEnd;
Report("Current batch position ="+ IntToStr( fNextPos ));
fPosFromEnd := GetBatchPos;
fLastPos := fPosFromEnd;
fStream.Position := fPosFromEnd;
SetLength( S, lrBatchSize );
SetLength( S, fStream.Read( S[1], lrBatchSize ) );
for i := 1 to Length(S) do
if S[i] = #10 then break;
fPosFromEnd := fPosFromEnd + i;
fStrings.Text := Copy(S, i + 1, Length(S) - i) ;
Report("batch read (direction=right to left) success, alloc. StrCount="+
IntToStr( fStrings.Count ) );
fBatchPos := 0;
end;
procedure TLogReader.BatchRead;
var
S : String;
i : integer;
begin
if CanRead then
try
Report("Trying to do batch read");
fStream.Position := fNextPos;
Report("Current batch position ="+ IntToStr( fNextPos ));
fLastPos := fNextPos;
SetLength( S, lrBatchSize );
SetLength( S, fStream.Read( S[1], lrBatchSize ) );
for i := Length( S ) - 3 downto 1 do
if S[i] = #10 then break;
fNextPos := fNextPos + i;
if i > 2 then
fStrings.Text := Copy( S, 1, i-2)
else
fStrings.Text := Copy( S, 1, i);
Report("batch read success, allocated stringcount="+IntToStr( fStrings.Count ) );
fBatchPos := 0;
except
on E:Exception do ShowException("BatchRead", ClassName, E)
end;
end;
function TLogReader.ReadString;
begin
Result := "";
if CanRead and (fStrings.Count > 0) then
begin
if fBatchPos < fStrings.Count then
begin
Result := fStrings.Strings[fBatchPos];
inc(fBatchPos);
if not fDontCheckMask then
if (pos(fMask, Result) = 0) and (fBatchPos < fStrings.Count) then
begin
inc( fErrors );
Result := ReadString;
end;
end
else
begin
if fSearching and fReverseLookup then
BatchReadFromEnd
else
BatchRead;
Result := ReadString;
end;
end;
fEof := Length(Result) = 0;
if not fEof then
fLastString := Result
end;
← →
Defunct © (2005-09-14 01:50) [19]Продолжение..
procedure TLogReader.SetLastLoadedString(const Value: String);
var
S: String;
begin
if Value <> "" then
try
Report("setting LLS to locate correct stream position");
Report("incoming LLS = "+Value);
fSearching := True;
if fReverseLookup then BatchReadFromEnd;
fLastLoadedString := Value; // будем искать эту строку в файле
if CanRead then
begin
S := ReadString;
while (Pos(fLastLoadedString, S)=0) and (not EOf) do
S := ReadString;
fStartPos := fLastPos;
end;
Report("LLS detected at stream postion = " + IntToStr( fLastPos ) );
Report("LLS`s batch position = " + IntToStr( fBatchPos ) );
finally
fSearching := false;
end
else
begin
Report("there is no selected LLS, stream will be read from begin");
end;
end;
function TLogReader.GetEndPos: Int64;
begin
if Assigned( fStream ) then
Result := fStream.Size
else
Result := 0;
end;
function TLogReader.GetPercentDone: integer;
begin
Result := 100;
if Assigned( fStream ) then
if EndPosition - StartPosition > 0 then
begin
Result := Round( Result * (fStream.Position - StartPosition)/
(EndPosition - StartPosition) )
end
end;
destructor TLogReader.Destroy;
begin
Report("exiting log reader");
FreeAndNil( fStream );
FreeAndNil( fStrings );
inherited;
end;
← →
Alexander Panov © (2005-09-14 02:02) [20]IceBeerg © (13.09.05 18:26)
Как вариант - разбить файл на куски приемлимого размера, каждому файлу дать имя, в которое входит значение начальной и конечной даты, обрабатывать нужный файл в зависимости от даты.
← →
Defunct © (2005-09-14 02:12) [21]Пример применения:
TMyLogReader = class( TLogReader)
public
function GetString: String;
constructor Create;override;
end;
constructor TMyLogReader.Create;
begin
inherited;
fDontCheckMask := True;
end;
function TMailLogReader.GetString: TMailRecord;
var
BackupLastString : String;
begin
BackupLastString := fLastString;
Result := ReadString;
if Result = "" then
fLastString := BackupLastString
end;
Для чтения с начала:var
LLS : String;
...
with TMyLogReader do
try
ReportBox := Memo1;
ReverseLookup := False;
FileName := "some.log"
LastLoadedString := "";
while not Eof do
begin
LLS := GetString;
...
end;
<Новая последняя прочитанная строка> := LastString;
finally
free;
end;
Для добавления новых записей с определенной позиции:var
LLS : String;
...
with TMyLogReader do
try
ReportBox := Memo1;
ReverseLookup := True;
FileName := "some.log"
LastLoadedString := <Сохраненная последняя прочитанная строка>;
while not Eof do
begin
LLS := GetString;
...
end;
<Новая последняя прочитанная строка> := LastString;
finally
free;
end;
← →
Defunct © (2005-09-14 02:22) [22]Работает этот LogReaded так:
1. читает порцию данных из стрима, которая определяется константой lrBactchSize в байтах. (оптимальнее всего ~1Mb) в TStrings
2. выкусывает обрывки строк из TStrings, и корректирует абсолютную позицию в файле для чтения следующей порции строк.
3. Ключевой метод - ReadString, последовательно выдает все строки открытого лог файла.
4. Читать лог файл можно с определенной позиции, которая определяется при задании "последней прочитанной строки" (свойство LastLoadedString), имеется два способа поиска позиции - с начала файла и с конца, задается свойством ReversLookup. (поиск с конца значительно ускоряет процесс поиска позиции искомой строки, если искомая строка находится ближе к концу лог файла).
← →
Defunct © (2005-09-14 02:25) [23]Defunct © (14.09.05 02:12) [21]
допустил пару ошибок в [21]:
function TMyLogReader.GetString: String;
with TMyLogReader.Create do
← →
kaif © (2005-09-14 02:28) [24]Я бы для начала разрезал этот файл на 400 файлов равной длины, как советует Panov.
И лазил бы лишь туда, куда мне интересно лазить. Например, по дате.
Читал бы с помощью TFileStream блоками по 32K.
Внутри блоков искал бы символ возврата каретки.
Если что-то полезное нашел бы в этих файлах - перегнал бы в базу данных, а все бесполезное выкинул бы к чертям.
← →
Defunct © (2005-09-14 02:36) [25]kaif © (14.09.05 02:28) [24]
Log файл может постоянно дописываться. Класс [18] предназначен для работы с постоянно открытым Log файлом, т.е. приложение ведет себе лог, а мы подчитываем себе в базу только новые записи при необходимости. Бить файл на много мелких imho абсолютно не нужно. Проще добавить в базу все что надо и стереть весь этот лог.
← →
Gydvin © (2005-09-14 06:27) [26]Попробуй это. Только надо знать кол-во строк в файле.
function GrabLine(const AFileName: string; ALine: Integer): string;
var
fs: TFileStream;
buf: packed array[0..4095] of Char;
bufRead: Integer;
bufPos: PChar;
lineStart: PChar;
tmp: string;
begin
fs := TFileStream.Create(AFileName, fmOpenRead);
try
Dec(ALine);
bufRead := 0;
bufPos := nil;
{ read the first line specially }
if ALine = 0 then
begin
bufRead := fs.Read(buf, SizeOf(buf));
if bufRead = 0 then
raise Exception.Create("Line not found");
bufPos := buf;
end else
while ALine > 0 do
begin
{ read in a buffer }
bufRead := fs.Read(buf, SizeOf(buf));
if bufRead = 0 then
raise Exception.Create("Line not found");
bufPos := buf;
while (bufRead > 0) and (ALine > 0) do
begin
if bufPos^ = #10 then
Dec(ALine);
Inc(bufPos);
Dec(bufRead);
end;
end;
{ Found the beginning of the line at bufPos... scan for end.
2 cases:
1) we"ll find it before the end of this buffer
2) it"ll go beyond this buffer and into n more buffers }
lineStart := bufPos;
while (bufRead > 0) and (bufPos^ <> #10) do
begin
Inc(bufPos);
Dec(bufRead);
end;
{ if bufRead is positive, we"ll have found the end and we can leave. }
SetString(Result, lineStart, bufPos - lineStart);
{ determine if there are more buffers to process }
while bufRead = 0 do
begin
bufRead := fs.Read(buf, SizeOf(buf));
lineStart := buf;
bufPos := buf;
while (bufRead > 0) and (bufPos^ <> #10) do
begin
Inc(bufPos);
Dec(bufRead);
end;
SetString(tmp, lineStart, bufPos - lineStart);
Result := Result + tmp;
end;
finally
fs.Free;
end;
delete(Result,length(Result),1);
end;
← →
Fay © (2005-09-14 06:51) [27]2 Alexander Panov © (14.09.05 2:02) [20]
Хрень получится. К тому же, если не ошибаюсь, 1C-у это не понравится.
Лучше создать файл со смещениями строк.
← →
KilkennyCat © (2005-09-14 08:00) [28]
> 1C-у это не понравится.
а не надо им говорить.
← →
Fay © (2005-09-14 08:21) [29]2 KilkennyCat © (14.09.05 8:00) [28]
Я ваще не спец по 1С Пр-е, но мне как-то запомнилось, что он матерится на изменение лога.
← →
KilkennyCat © (2005-09-14 08:58) [30]
> [29] Fay © (14.09.05 08:21)
то есть, он отслеживает изменение ТАКОГО лога?! когда ж он основным делом занят? :))
← →
Fay © (2005-09-14 09:38) [31]KilkennyCat © (14.09.05 8:58) [30]
Он отслеживает при "запуске", после чего файл занят монопольно.
← →
Игорь Шевченко © (2005-09-14 10:00) [32]Fay © (13.09.05 23:38) [17]
> >> Быстрее все равно не сделаешь
> Спорим? 8)
А как меряться будешь ? :)
Я уже проводил подобные измерения, ReadLn работает достаточно быстро.
← →
Fay © (2005-09-14 10:03) [33]2 Игорь Шевченко © (14.09.05 10:00) [32]
>> ReadLn работает достаточно быстро.
Но не ultimate 8)
← →
Игорь Шевченко © (2005-09-14 10:15) [34]Fay © (14.09.05 10:03) [33]
Хорошо, давай ты проведешь эксперимент - создашь текстовый файл на 40 Гб, и почитаешь его разными способами. Только перед каждым чтением необходимо выключать компьютер, чтобы исключить побочный эффект кеширования и аппаратной буферизации :)
Результаты можно выложить в этой ветке.
← →
Fay © (2005-09-14 10:20) [35]2 Игорь Шевченко © (14.09.05 10:15) [34]
У меня есть только 17Gb
← →
IceBeerg © (2005-09-14 10:21) [36]PVOzerski © (13.09.05 19:03) [13]
Я не знаю формат лога 1С - может, там не #13#10 :)
Нормальный там формат - #13#10
Alexander Panov © (14.09.05 2:02) [20]
Как вариант - разбить файл на куски приемлимого размера
тоже идея
kaif © (14.09.05 2:28) [24]
Читал бы с помощью TFileStream блоками по 32K.
мне рекомендовали по 8 или 16К
Defunct © (14.09.05 2:36) [25]
Log файл может постоянно дописываться
именно это и происходит
Fay © (14.09.05 6:51) [27]
1C-у это не понравится
а хто его спашифать будет?
Fay © (14.09.05 8:21) [29]
Я ваще не спец по 1С Пр-е, но мне как-то запомнилось, что он матерится на изменение лога.
молодец, честно признался, нихрена он не матрится на изменеия лога. Я свои действия скрывал так: открыл лог в FAR"е убил нужные строки и вышел с сохранением и никаких тело-прграммо-движений по этому поводу в 1С не наблюдалось...
Fay © (14.09.05 9:38) [31]
после чего файл занят монопольно
нет, не занять монопольно, а то как бы 6 юзверей туда инфу писали?
Игорь Шевченко © (14.09.05 10:00) [32]
Я уже проводил подобные измерения, ReadLn работает достаточно быстро
наверное всетаки воспользуюсь ReadLn и нафик изобретать велосипед...
И ВООБЩЕ МУЖИКИ, ВСЕМ БОЛЬШОЕ СПАСИБО, ЗА РЕАЛИЗАЦИИ (КОД) ОТДЕЛЬНОЕ! БУДУ ПРОБОВАТЬ...
← →
Игорь Шевченко © (2005-09-14 10:32) [37]Fay © (14.09.05 10:20) [35]
Вот давай ты 10 Гб выделишь под файл и будешь развлекаться. Мне интересно, насколько оно быстрее получится в твоем варианте.
← →
IceBeerg © (2005-09-14 10:52) [38]Качайте реальный - 44045783 байт, в RAR архиве 2832442 байт. www.icebeerg.newmail.ru/1cv7.rar
← →
Fay © (2005-09-14 10:55) [39]2 IceBeerg © (14.09.05 10:52) [38]
Не наш размер 8)
Но данные вполне пойдут для заполнения
← →
Игорь Шевченко © (2005-09-14 11:03) [40]IceBeerg © (14.09.05 10:52) [38]
Вроде ты другой размер указывал в начале ?
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2005.10.09;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.015 c