![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Нужен быстрый способ разбора строк [D7, WinXP]
Sens © (2004-08-02 11:01:00)Доброго Всем дня.
Возникла следующая проблема:
Есть лог файл сквида ( это одна строка)
1088655074.210 4710 192.168.1.249 TCP_MISS/200 406 GET http://www.internet-optimizer.com/conf/signin/?- DIRECT/65.39.191.5 text/html
Необходимо разобрать строку примерно по таким полям:
Время: 1088655074.210 <br>
Размер: 4710 <br>
Адрес запрашиваемого: 192.168.1.249 <br>
TCP_MISS/200 406 <br>
Метод (направление запроса): GET <br>
Адрес: http://www.internet-optimizer.com/conf/signin/?- DIRECT/65.39.191.5 text/html <br>
преобразовать это дело в базу (использую Access) для создания отчета по использованию интернет.
разделители между полями (в строке) пробелы, количество в разных строках разное (использовать какой либо формат для разбора не получается). На данный момент разбираю строку по алгоритму:
1. От позиции начала до первого пробела <br>
2. Запихиваю первое значение в базу (в нужное поле)<br>
3. Отсекаю все пробелы до первого не пробела <br>
4. Обрезаю строку.<br>
5. Повторяю цикл до конца строки.<br>
6. Повторяю цикл до конца файла.<br>
Все было хорошо пока файл был меньше 10МБ,на его преобразование уходило ~10-15 мин, но теперь он за месяц выростает до 60-100 МБ, и наш компик загибается на долгое время (2-4 часов).
Подскажите пожалуйста более быстрый алгоритм разбора строки (все упирается в него).
panov (2004-08-03 12:16:00) [1]>Sens © (03.08.04 10:02) [18]
Увы, Access я не использую... поэтому не смогу этим способом воспользоваться...
sniknik (2004-08-03 11:02:00) [2]можно попробовать перенести все запросом, правда переменное количество пробелов приводит в некоторое смущение ;о)).
зато если получится будет самый быстрый вариант.
слей часть лога на мой мыл, попробую вечером, если время будет. (без гарантий что рещу есно. и постарайся чтобы в часть попало все инвариантное (поближе к реальности ;о)))
да и сколько у тебя идет обработка? может и парится не стоит, а и так устраивает, как уже решил.
REA (2004-08-03 10:49:00) [3]>и немалое(если не основное время) занимает добавление записей в DBF...
используй прямой монопольный доступ к таблице через сторонние компоненты (например Degisy Data)
Sens (2004-08-03 10:02:00) [4]to panov © (02.08.04 11:51) [11]
Нашел быстрый (ооочень быстрый) способ внесения данных в БД.
Не использую никаких компонент.
Готовлю файл (из файла описанного в сабже):
01.07.2004 6:11:14;192.168.1.249; http://...;5116
01.07.2004 6:11:16;192.168.1.249; http://...;1351
После чего делаю следующее:
// Открываем Access
Label2.Caption:='Opening MS Access...';
fmMain.Refresh;
Access := CreateOleObject('Access.Application');
Access.Visible := False;
Label2.Caption:='Opening '+CurDir+'\log.mdb';
fmMain.Refresh;
Access.OpenCurrentDatabase(CurDir+'\log.mdb', True);
Access.DoCmd.DeleteObject (acTable, 'Log');
Label2.Caption:='Transfering to '+CurDir+'\log.mdb';
fmMain.Refresh;
Access.DoCmd.TransferText (acImportDelim, '', 'log', CurDir+'\111.txt', False, '');
Label2.Caption:='Closeing '+CurDir+'\log.mdb';
fmMain.Refresh;
Access.CloseCurrentDatabase;
Access.Quit(acQuitSaveAll);
Label2.Caption:='Transfer complited succesfuly!!!';
fmMain.Refresh;
Рекомендую использовать такой способ вместо вставки в DBF. Прирост в скорости гарантирован в десятки раз!!!
У меня на обработку лог файла 63Мб (530956 строк) и конвертирования его в Access на 2600+ Barton уходит 9 сек.
Если есть желание, могу слить конвертор (выложить некуда).
Sens (2004-08-02 16:48:00) [5]Написал, ускорение - УХ...
БЫЛО такое (Звеняйте за С++)
for (int j=0;j<fmMain->Memo1->Lines->Count-1;j++)
{
s=fmMain->Memo1->Lines->Strings[j];
int k=0;
sRes=s.SubString(0,s.Pos(".")-1);
CDate=DateTimeToStr(UnixToDateTime(StrToInt(sRes)+7200));
Record.Time=CDate;
int i=s.Pos(".");
s=s.SubString(i,s.Length());
//=============DateTime===============================
i=0,k++;
DM->LogCommand->
CommandText="INSERT INTO Log (RecTime,SendSize,UserIP,QueryType,ResiveSize,URL)";
DM->LogCommand->
CommandText=DM->
LogCommand->CommandText+"VALUES (:RecTime,:SendSize,:UserIP,";
DM->LogCommand->
CommandText=DM->
LogCommand->CommandText+":QueryType,:ResiveSize,:URL)";
DM->LogCommand->Parameters->Items[0]->Value=Record.Time;
while (k!=8)
{
sRes=s.SubString(i,s.Pos(" ")-1);
i=s.Pos(" ");
while (s[i]==' ')
{
i++;
}
s=s.SubString(i,s.Length());
i=0;
switch (k)
{
case 1: break;
case 2: DM->LogCommand->Parameters->Items[1]->Value=StrToInt(sRes); break;
case 3: DM->LogCommand->Parameters->Items[2]->Value=sRes; break;
case 4: DM->LogCommand->Parameters->Items[3]->Value=sRes; break;
case 5: DM->LogCommand->Parameters->Items[4]->Value=StrToInt(sRes); break;
case 6: break;
case 7: DM->LogCommand->Parameters->Items[5]->Value=sRes.SubString(0,150); break;
}
k++;
}
DM->LogCommand->Execute();
fmMain->PB->Position=j;
СТАЛО ТАКОЕ!
procedure TfmMain.BitBtn1Click(Sender: TObject);
var
OD:TOpenDialog;
F,ResF: TextFile;
S,GET2,URL,URL2,IP,TempURL: string[200];
CDate:TDateTime;
i,PosMISS,PosGET,IPsp,URLpos1,URLpos2:integer;
begin
OD:=TOpenDialog.Create(Owner);
if OD.Execute then
Begin
AssignFile(F, OD.FileName); { File selected in dialog }
AssignFile(ResF,'111.txt');
Rewrite(ResF);
Reset(F);
i:=1;
while not Eof(F) do
begin
Readln(F, S);
PosMISS:=Pos('/',S)+4;
PosGET:=Pos('GET',S)-1;
if PosGET<1 then PosGET:=Pos('POST',S)-1;
URLpos1:=Pos('http://',S);
URL:= Copy(S,URLpos1,50);
TempURL:=Copy(S,URLpos1+7,50);
URLpos2:=Pos('/',TempURL);
URL:=Copy(URL,0,URLpos2+6);
IP:=Copy(S,23,13);
IPsp:=Pos(' ',IP);
if IPsp>3 then IP:=Copy(IP,0,IPsp-1);
if PosGET>1 then
begin
CDate:=UnixToDateTime(StrToInt(Copy(S,0,10))+7200);
GET2:=Copy(S,PosMISS,PosGET-PosMISS);
Writeln(ResF,DateTimeToStr(CDate)+';'+IntToStr(StrToInt(Copy(S,15,7)))+';'+IP+';'+IntToStr(StrToInt(GET2))+';'+URL);
inc(i);
Label1.Caption:=IntToStr(i);
fmMain.Refresh;
end;
end;
CloseFile(F);
CloseFile(ResF);
//====================================
OD.Free;
//====================================
End;
end;
Вывод:
Для разбора строк необходимо пользоваться функциямиPosиCopy, а не заниматься переприсваиванием строк!!!
Всем спасибо за участие.
sdw_syscoder (2004-08-02 12:27:00) [6]Удалено модератором
Примечание: Личная переписка и офтопик
panov (2004-08-02 12:11:00) [7]Сразу говорю, что это не оптимизировано и реализовано неоптимально. это полусырой работающий вариант.
Функция обрабатывает 1 строку журнала WinGate(любого).
procedure TfWLogMain.ParseAndInsertLog(const Src: String;aFName: String);
function QU(const src: String): String;
begin
Result := Src;
// Result := ''''+Src+'''';
end;
var
s,s1: String;
pz: Integer;
YY,MM,DD,HH,NN,SS: String;
IP,Addr,Name,Sess: String;
qType: String;
qAddr: String;
SentToClient,RecFromClient,SentForClient,RecForClient,sec: Integer;
idDate,idFile,idType,idUser,idService: Integer;
begin
s := Src;
s := StringReplace(s,'"','#',[rfReplaceAll]);
s := StringReplace(s,'''','#',[rfReplaceAll]);
MM := Copy(s,1,2);
DD := Copy(s,4,2);
YY := Copy(s,7,2);
HH := Copy(s,10,2);
NN := Copy(s,13,2);
SS := Copy(s,16,2);
Delete(s,1,18);
pz := Pos(#9,s);
if pz>0 then
begin
IP := Copy(s,1,pz-1);
if not (IP[1] in ['0'..'9']) then Exit;
Delete(s,1,pz);
end
else
begin
if Copy(s,1,28)='Service started successfully'
then CurrWGSess := NewId('idSessWG');
IP:='';
end;
pz := Pos(#9,s);
if pz>0 then
begin
Name := Copy(s,1,pz-1);
Delete(s,1,pz);
end
else Name:='';
pz := Pos(#9,s);
if pz>0 then
begin
Sess := Copy(s,1,pz-1);
Delete(s,1,pz);
end
else Sess:='';
// if sess='0001219699' then
// begin
// ShowMEssage('!');
// end;
pz := Pos(#9,s);
if pz>0 then
begin
qType := Copy(s,1,pz-1);
Delete(s,1,pz);
end
else
begin
if Copy(s,1,10)='Terminated'
then qType:='Terminated'
else
begin
if Copy(s,1,8)='Created:'
then qType:='Created:'
else
if Copy(s,1,6)='Failed'
then qType:='Failed'
else qType := '';
end
end;
if qType='Requested:' then
begin
Addr := Trim(s);
end
else Addr :='';
if qType='Traffic' then
begin
pz := Pos(#9,s);
if pz>0 then
begin
SentToClient := StrToInt(Copy(s,1,pz-1));
Delete(s,1,pz);
end
else SentToClient:=0;
pz := Pos(#9,s);
if pz>0 then
begin
RecFromClient := StrToInt(Copy(s,1,pz-1));
Delete(s,1,pz);
end
else RecFromClient:=0;
pz := Pos(#9,s);
if pz>0 then
begin
SentForClient := StrToInt(Copy(s,1,pz-1));
Delete(s,1,pz);
end
else SentForClient:=0;
pz := Pos(#9,s);
if pz>0 then
begin
RecForClient := StrToInt(Copy(s,1,pz-1));
Delete(s,1,pz);
end
else RecForClient:=0;
sec := StrToInt(Copy(s,1,Length(s)-1));
Delete(s,1,pz);
end
else
begin
// Exit;
sec := 0;
SentToClient:=0;
RecFromClient:=0;
SentForClient:=0;
RecForClient:=0;
end;
if (qType='Requested:') or (qType='Traffic') then
begin
t.Append;
idDate := CheckDate(YY+MM+DD);
idUser := CheckUser(Name);
idType := CheckType(qType);
idService := CheckService(aFName);
// Form1.t.FieldValues['dd'] := qu(DD);
// Form1.t.FieldValues['mm'] := qu(MM);
// Form1.t.FieldValues['yy'] := qu(YY);
t.FieldValues['idSessWG'] := CurrWGSess;
t.FieldValues['idDate'] := idDate;
t.FieldValues['hhnnss'] := qu(HH+NN+SS);
// Form1.t.FieldValues['nn'] := qu(NN);
// Form1.t.FieldValues['ss'] := qu(SS);
t.FieldValues['ip'] := qu(IP);
t.FieldValues['idUser'] := idUser;
t.FieldValues['idsess'] := Sess;
// Form1.t.FieldValues['qType'] := qu(qType);
t.FieldValues['idType'] := idType;
t.FieldValues['idService'] := idService;
t.FieldValues['Addr'] := qu(Addr);
t.FieldValues['SentTo'] := SentToClient;
t.FieldValues['RecFrom'] := RecFromClient;
t.FieldValues['SentFor'] := SentForClient;
t.FieldValues['RecFor'] := RecForClient;
t.FieldValues['Sec'] := sec;
t.Post;
end;
end;
Рамиль (2004-08-02 12:10:00) [8]А в загрузке файла каждый раз полностью есть какой то глубинный смысл? Не лучше запоминать позицию и грузить только новые записи.
Anatoly Podgoretsky (2004-08-02 12:03:00) [9]Насчет ReadLn может оказаться что разделители строк Юниксовские
Насчет загрузки в StringList рекомендую посмотреть выделеную память, думаю это охладит.
Лучше если это будет буферизированое чтение с разбором на строки или загрузка всего файла как единого целого в одну единственную строку и далее проход по пробелам, без какого либо деления, только копирование частей. При достаточном объеме оперативки и быстрых дисках это будет очень быстро. Если памяти мало, то буферизирование чтение и разбивка на строки.
Тогда 100 мб файл можно будет обработать за минуту/две максимум и остальное время уйдет на добавление в базу.
Sens (2004-08-02 12:03:00) [10]to panov
А можно алгоритм разбора или кусочек кода?
panov (2004-08-02 11:51:00) [11]У меня сделан разбор подобного лога(WinGate).
Я не оптимизировал особо, но на локальной БД ты не получишь приличного ускорения. У меня обработка файлов общим размером около 500Мб идет около часа, и немалое(если не основное время) занимает добавление записей в DBF...
REA (2004-08-02 11:51:00) [12]1) Сдается мне, что это уже кто-то делал
2) Может использовать grep?
Sandman25 (2004-08-02 11:49:00) [13]>А как считаете
"Практика - критерий истины" :)
Sens (2004-08-02 11:46:00) [14]to sdw_syscoder
У меня нет никаких потоков. Писал прогу давно и на C++ Builder, теперь хочу ускорить и написать на Delphi 7.
to Sandman25
/О боже. Readln(S), где S - AnsiString. Все./
Я бы заменил "О боже" на "леньтяй" или "ламер". :))
Попробую Readln(S). Спасибо.
А как считаете, если сначала пройтись по файлу и заменить "зоны пробелов" на один "Таб", а потом конвертнуть в Access текстовый файл скорость увеличится?
panov (2004-08-02 11:39:00) [15]>Sens © (02.08.04 11:01)
В данном случае файл лучше(и намного) читать построчно(см. Sandman25 © (02.08.04 11:36) [4]), потому что на загрузку файла и обработку потребуется выделение большого количества памяти, что может привести еще и к свопингу...
default (2004-08-02 11:37:00) [16]Sens © (02.08.04 11:32) [3]
обрезать только ничего не надо...
посмотри можно-ли у тебя какое-то(ие-то) поле(я) сделать постоянного размера, только в этом случае разбор будет самым быстрым
Sandman25 (2004-08-02 11:36:00) [17][2] sdw_syscoder (02.08.04 11:28)
Ничего не знаю. У автора про потоки ничего не сказано :)
Sandman25 (2004-08-02 11:36:00) [18]> Я обьявляю TStringList и гружу в него файл.
О боже. Readln(S), где S - AnsiString. Все.
Sens (2004-08-02 11:32:00) [19]Еще пример:
1067162459.277 261 192.168.1.5 TCP_MISS/200 363 GET http://...
1067162461.464 2322 192.168.1.5 TCP_MISS/200 7759 GET http:...
Второй параметр во второй строке отстоит от первого параметра уже на разное кол-во пробелов, использование copyизбавит от необходимости "обрезания" но не избавит от необходимости считать пробелы.
Может подскажите какой-то способ более лучшего доступа к файлу или размещения его в памяти? Я обьявляю TStringList и гружу в него файл.
Я в полной Ж. Моя прога за 1.5 часа только 40% разобрала... админ материться. ;)
sdw_syscoder (2004-08-02 11:28:00) [20]Sandman25 © (02.08.04 11:07) [1]
Дело не в этом, а в организации потоков.
А можно у Вас поинтересоваться как Вы организовали этот разбор строк, потому что у меня аналогичная проблема, но только с гораздо меньшим объёмом данных.
Моя ветка - http://www.delphimaster.ru/cgi-bin/forum.pl?id=1091395450&n=0