Форум: "Основная";
Текущий архив: 2004.08.15;
Скачать: [xml.tar.bz2];
ВнизНужен быстрый способ разбора строк Найти похожие ветки
← →
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;
Скачать: [xml.tar.bz2];
Память: 0.53 MB
Время: 0.038 c