Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.08.15;
Скачать: CL | DM;

Вниз

Нужен быстрый способ разбора строк   Найти похожие ветки 

 
Sens ©   (2004-08-02 11:01) [0]

Доброго Всем дня.

Возникла следующая проблема:
Есть лог файл  сквида (это одна строка)

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 часов).

Подскажите пожалуйста более быстрый алгоритм разбора строки (все упирается в него).


 
Sandman25 ©   (2004-08-02 11:07) [1]

Не надо ничего отсекать. Надо все время использовать Copy(S, StartIndex, ALength) и только правильно рассчитывать последние два параметра.
Тогда не будет лишних операций с исходной строкой.


 
sdw_syscoder   (2004-08-02 11:28) [2]

Sandman25 ©   (02.08.04 11:07) [1]

Дело не в этом, а в организации потоков.

А можно у Вас поинтересоваться как Вы организовали этот разбор строк, потому что у меня аналогичная проблема, но только с гораздо меньшим объёмом данных.

Моя ветка - http://delphimaster.net/view/1-1091395450/


 
Sens ©   (2004-08-02 11:32) [3]

Еще пример:
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% разобрала... админ материться. ;)


 
Sandman25 ©   (2004-08-02 11:36) [4]

> Я обьявляю  TStringList и гружу в него файл.

О боже. Readln(S), где S - AnsiString. Все.


 
Sandman25 ©   (2004-08-02 11:36) [5]

[2] sdw_syscoder   (02.08.04 11:28)

Ничего не знаю. У автора про потоки ничего не сказано :)


 
default ©   (2004-08-02 11:37) [6]

Sens ©   (02.08.04 11:32) [3]
обрезать только ничего не надо...
посмотри можно-ли у тебя какое-то(ие-то) поле(я) сделать постоянного размера, только в этом случае разбор будет самым быстрым


 
panov ©   (2004-08-02 11:39) [7]

>Sens ©   (02.08.04 11:01)

В данном случае файл лучше(и намного) читать построчно(см. Sandman25 ©   (02.08.04 11:36) [4]), потому что на загрузку файла и обработку потребуется выделение большого количества памяти, что может привести еще и к свопингу...


 
Sens ©   (2004-08-02 11:46) [8]

to sdw_syscoder
У меня нет никаких потоков. Писал прогу давно и на C++ Builder, теперь хочу ускорить и написать на Delphi 7.

to Sandman25
/О боже. Readln(S), где S - AnsiString. Все./
Я бы заменил "О боже" на "леньтяй" или "ламер". :))
Попробую Readln(S). Спасибо.

А как считаете, если сначала пройтись по файлу и заменить "зоны пробелов" на один "Таб", а потом конвертнуть в Access текстовый файл скорость увеличится?


 
Sandman25 ©   (2004-08-02 11:49) [9]

>А как считаете

"Практика - критерий истины" :)


 
REA ©   (2004-08-02 11:51) [10]

1) Сдается мне, что это уже кто-то делал
2) Может использовать grep?


 
panov ©   (2004-08-02 11:51) [11]

У меня сделан разбор подобного лога(WinGate).
Я не оптимизировал особо, но на локальной БД ты не получишь приличного ускорения. У меня обработка файлов общим размером около 500Мб идет около часа, и немалое(если не основное время) занимает добавление записей в DBF...


 
Sens ©   (2004-08-02 12:03) [12]

to panov
А можно алгоритм разбора или кусочек кода?


 
Anatoly Podgoretsky ©   (2004-08-02 12:03) [13]

Насчет ReadLn может оказаться что разделители строк Юниксовские
Насчет загрузки в StringList рекомендую посмотреть выделеную память, думаю это охладит.
Лучше если это будет буферизированое чтение с разбором на строки или загрузка всего файла как единого целого в одну единственную строку и далее проход по пробелам, без какого либо деления, только копирование частей. При достаточном объеме оперативки и быстрых дисках это будет очень быстро. Если памяти мало, то буферизирование чтение и разбивка на строки.
Тогда 100 мб файл можно будет обработать за минуту/две максимум и остальное время уйдет на добавление в базу.


 
Рамиль ©   (2004-08-02 12:10) [14]

А в загрузке файла каждый раз полностью есть какой то глубинный смысл? Не лучше запоминать позицию и грузить только новые записи.


 
panov ©   (2004-08-02 12:11) [15]

Сразу говорю, что это не оптимизировано и реализовано неоптимально. это полусырой работающий вариант.
Функция обрабатывает 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;


 
sdw_syscoder   (2004-08-02 12:27) [16]

Удалено модератором
Примечание: Личная переписка и офтопик


 
Sens ©   (2004-08-02 16:48) [17]

Написал, ускорение - УХ...

БЫЛО такое (Звеняйте за С++)

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, а не заниматься переприсваиванием строк!!!

Всем спасибо за участие.


 
Sens ©   (2004-08-03 10:02) [18]

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 сек.
Если есть желание, могу слить конвертор (выложить некуда).


 
REA ©   (2004-08-03 10:49) [19]

>и немалое(если не основное время) занимает добавление записей в DBF...

используй прямой монопольный доступ к таблице через сторонние компоненты (например Degisy Data)


 
sniknik ©   (2004-08-03 11:02) [20]

можно попробовать перенести все запросом, правда переменное количество пробелов приводит в некоторое смущение ;о)).
зато если получится будет самый быстрый вариант.

слей часть лога на мой мыл, попробую вечером, если время будет. (без гарантий что рещу есно. и постарайся чтобы в часть попало все инвариантное (поближе к реальности ;о)))
да и сколько у тебя идет обработка? может и парится не стоит, а и так устраивает, как уже решил.


 
panov ©   (2004-08-03 12:16) [21]

>Sens ©   (03.08.04 10:02) [18]

Увы, Access я не использую... поэтому не смогу этим способом воспользоваться...



Страницы: 1 вся ветка

Текущий архив: 2004.08.15;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.044 c
4-1088692618
Интересующийся
2004-07-01 18:36
2004.08.15
Как узнать какие сет. карты установлены?


6-1086540204
RAshka
2004-06-06 20:43
2004.08.15
опять таки - Как узнать рабочую группу компьютера? НО.......


3-1090509127
djoni21
2004-07-22 19:12
2004.08.15
Проблемы с dbExpress при переносе приложения


1-1090965124
Ведьмак
2004-07-28 01:52
2004.08.15
toolbar для IE


4-1088622658
DeadMeat
2004-06-30 23:10
2004.08.15
Память и Время