Форум «Всё о Паскале» _ Задачи _ Анализ log-файла...
Автор: Роман 28.05.2011 22:23
Собственно вот задание: Написать программу по разбивке и анализу содержимого файла отчета работы ПО (log-файла). Необходимо проанализаровать и подсчитать запросы, а также вывести их в алфавитном порядке с подсчетом колическтва повторений.
Программа должна выдавать отчет о своей работе в виде:
Бухгалтерский учет - 25 Бухгалтерский журнал - 30 Экономика Томска - 6 Экономика Якутии - 1 ...
Пример файла отчета работы программы wslog.txt Сортировку запросов необходимо проводить по CGI-переменным S21STR= S21ALL= Проблема в том что у меня во второй части программы (подсчет и сортировка самих текстовых запросов) переполняется массив... а вот как сделать без массива не пойму...
program Laba_2; uses crt; var s2:string; f,f1:text; g:char; a,x,i,j,q,p:integer; zap,m:string; n:array [1..1000] of byte; zn:array [1..1000]of string[40]; s,s1:string[10]; begin clrscr; assign(f,'wslog.txt'); assign(f1,'output.txt'); reset(f); rewrite(f1); x:=0; a:=0; q:=0; Readln(f,s1); Repeat Readln(f,s); if (s<>s1) and (s1<>'') then {если строки не одинаковы,то считает кол-во запросов в день} begin Writeln(f1,s1,' - ',a); x:=x+a; a:=0; s1:=s; end else a:=a+1;{иначе суммирует кол-во одинаковых строк} until eof(f); Writeln(f1,' vsego zaprosov :'); Writeln(f1,x); Reset(f); {S21STR} Repeat Readln(f,s2); a:=pos('S21STR=',s2); if a<>0 then begin a:=a+7; zap:=''; if S2[a]='' then q:=q+1 else Repeat zap:=zap+s2[a]; {считывает послед.символы после 'S21STR=' до &} a:=a+1; Until s2[a]='&'; for i:=1 to 1000 do begin if zap=zn[i] then begin n[i]:=n[i]+1; break; end; if zn[i]='' then begin zn[i]:=zap; inc(n[i]); break; {считает кол-во запросов} end; end; end; until eof(f);
{S21ALL аналогично}
Repeat Readln(f,s2); a:=pos('S21ALL=',s2); if a<>0 then begin a:=a+7; zap:=''; if s2[a]='' then q:=q+1 else Repeat zap:=zap+s2[a]; a:=a+1; Until s2[a]='&'; for i:=1 to 1000 do begin if zap=zn[i] then begin n[i]:=n[i]+1; break; end; if zn[i]='' then begin zn[i]:=zap; inc(n[i]); break; end; end; end; until eof(f); {сортирует строки} for i:=1000 downto 1 do for j:=1 to i-1 do if zn[j]>zn[j+1] then begin {} m:=zn[j+1]; p:=n[j+1]; zn[j+1]:=zn[j]; n[j+1]:=n[j]; zn[j]:=m; n[j]:=p; end; {выводит рез-ты в файл} writeln(f1,' ',' ',q);{выводит кол-во пустых запрсов} for i:=1 to 1000 do if zn[i]<>'' then writeln(f1,zn[i],' ',n[i]); close(f); close(f1); end.
Проблема в том что у меня во второй части программы (подсчет и сортировка самих текстовых запросов) переполняется массив
проблема возникнет раньше. К примеру, в твоем файле, который прикреплен - строка №1034 является проблемной. Ты прочтешь ее из файла, найдешь в ней подстроку 'S21STR=', а вот символа '&' уже не найдешь. Он вне досягаемости в Турбо-Паскале. Потому что находится на позиции №258 в строке, а прочтешь ты только 255 (больше в String просто не влезет). В итоге получишь банальный вылет программы.
Отсюда первый вопрос: уверен, что хочешь продолжать именно с использованием Турбо-Паскаля, и изобретать костыли, вместо того, чтоб взять нормальный компилятор и начать решать задачу? В зависимости от твоего ответа будем думать дальше: либо как обойти эти ограничения, либо... Какой компилятор использовать взамен (хотя тут, конечно, решение напрашивается само собой)...
Автор: Роман 29.05.2011 13:53
Цитата(IUnknown @ 28.05.2011 20:07)
проблема возникнет раньше. К примеру, в твоем файле, который прикреплен - строка №1034 является проблемной. Ты прочтешь ее из файла, найдешь в ней подстроку 'S21STR=', а вот символа '&' уже не найдешь. Он вне досягаемости в Турбо-Паскале. Потому что находится на позиции №258 в строке, а прочтешь ты только 255 (больше в String просто не влезет). В итоге получишь банальный вылет программы.
Отсюда первый вопрос: уверен, что хочешь продолжать именно с использованием Турбо-Паскаля, и изобретать костыли, вместо того, чтоб взять нормальный компилятор и начать решать задачу? В зависимости от твоего ответа будем думать дальше: либо как обойти эти ограничения, либо... Какой компилятор использовать взамен (хотя тут, конечно, решение напрашивается само собой)...
ну можно попробовать другой компилятор. Как я понял вы имели ввиду FPC?
Автор: IUnknown 29.05.2011 14:31
Да, я именно его имел в виду. Перекомпилируй свою программу в FPC с ключом {$H+} и увеличенным размером массивов (я сделал
const maxsize = 2000; // ... n:array [1..maxsize] of byte; zn:array [1..maxsize]of string[40]; // и ниже по тексту все 1000 заменил на maxsize
), и чуть-чуть поправь вот этот цикл:
a:=pos('S21ALL=',s2); if a<>0 then begin a:=a+7; zap:=''; if s2[a]='' then q:=q+1 else Repeat zap:=zap+s2[a]; a:=a+1; Until s2[a]='&'; // <--- Вот это очень нехорошо...
, если в строке после S21STR не будет амперсанда - будет вылет опять же (такая строка - под номером 205 в твоем файле). Надо в Until добавить условие, чтоб проверялся конец строки:
Until (a > length(s2)) or (s2[a]='&');
Ну, или запоминать в целочисленной переменной длину строки (чтоб каждый раз ее не вычислять), и сравнивать со значением этой переменной...
Убедись, что программа работает и выдает правильный результат. И когда результат будет правильным, таким, который тебе нужен, замени этот ужасный массив из нескольких тысяч элементов на что-нибудь типа TStringList, там тебе не придется бегать туда-сюда самописными циклами и проверять, есть ли "оно" уже в списке (для этого существуют специальные методы), и занимать это будет ровно столько места, сколько нужно, а не "с огромным запасом".
Хотя, по хорошему, и искать Pos-ом строки в FPC не нужно. Есть регулярные выражения, вытягивать из строки можно что хочешь и как хочешь.
Автор: sheka 29.05.2011 15:09
Цитата
Хотя, по хорошему, и искать Pos-ом строки в FPC не нужно. Есть регулярные выражения, вытягивать из строки можно что хочешь и как хочешь.
Можно подробнее?
Автор: IUnknown 29.05.2011 15:53
Что именно? Про регулярки? Есть модуль regexpr вот в этой папке: \FPC\2.4.2\units\{target}\regexpr, в нем присутствует все необходимое для работы с регэкспами. Можно прикрутить известный дельфийский модуль, если функционала regexpr не хватает.
Согласись, проще за один проход вытащить из строки дату/время/ключевые слова, чем бегать в поисках разных подстрок Pos-ом. А ведь при использовании Pos надо еще найти конец искомого текста, с чем как раз у автора были проблемы. Регулярное выражение от этой необходимости избавляет, сразу будет найден кусок текста, расположенный между определенными словами или символами.
Автор: IUnknown 29.05.2011 23:10
Так... Ну, регэкспы в FPC - такие, что лучше б их вообще не было, поэтому...
...вот что получилось:(Показать/Скрыть)
{$mode objfpc} {$H+} uses classes, strutils;
// Стандартная Sort у меня почему-то напрочь отказалась работать правильно, // пришлось воспользоваться спецсредствами. function MySort(List: TStringList; Index1, Index2: Integer): Integer; begin if List[Index1] > List[Index2] then exit(1) else if List[Index1] = List[Index2] then exit(0); exit(-1); end;
procedure PrintData (var f : text; s : string; count : integer); begin writeln(f, s + ' -> ', count); end; procedure PrintQuery (var f : text; s : string; count : integer); begin writeln(f, '"' + s + '" -> ', count); end;
type PrintProc = procedure(var f : text; s : string; count : integer);
procedure Process (L : TStringList; var f : text; proc : PrintProc); var start, count : integer; begin start := 0; while start < L.Count do begin count := 1; while (start + count < L.Count) and (L[start] = L[start + count]) do inc(count); proc (f, L[start], count); inc(start, count); end; end;
while not eof (f) do begin readln(f, s); if s[1] <> '<' then lstData.Add (copy (s, 1, 10));
// queries for i := 1 to 2 do begin start := Pos (Tags[ i ], s); if start > 0 then begin finish := PosEx('&', s, start); if finish = 0 then finish := length(s) + 1; lstQueries.Add(copy (s, start + Length(Tags[ i ]), finish - start - Length(Tags[ i ]))); end; end;
end; Process (lstData, fout, @PrintData); LstQueries.CustomSort(@MySort); Process (lstQueries, fout, @PrintQuery);
lstData.Free; lstQueries.Free;
close(fout); close(f); end.
Всё остальное (всего запросов, количество пустых запросов, что там еще нужно - добавь сам). Кстати, твоя программа некорректно считала количество дат. Посмотри внимательно, сколько строк в логе, и сколько тебе показывает "всего запросов" - увидишь о чем я.
Автор: Роман 30.05.2011 0:04
Спасибо, все перекомпилировал, все работает.
lstQueries.Add(copy (s, start + Length(Tags[ i ]), finish - start - Length(Tags[ i ])));
Process (lstData, fout, @PrintData); LstQueries.CustomSort(@MySort); Process (lstQueries, fout, @PrintQuery); lstData.Free; lstQueries.Free;
function MySort(List: TStringList; Index1, Index2: Integer): Integer; begin if List[Index1] > List[Index2] then exit(1) else if List[Index1] = List[Index2] then exit(0); exit(-1); end;
Можно поподробнее как это работает?)
Автор: IUnknown 31.05.2011 22:29
Цитата
Можно поподробнее как это работает?)
Что именно? Функция MySort - это самописная функция сортировки двух строк, которые находятся в StringList-е. Она должна возвращать 0, если строки равны, 1 - если первая строка больше второй, и (-1) - если наоборот, вторая строка больше первой. Потом, такая функция передается параметром в метод CustomSort, и производится сортировка строк StringList-а (все, что надо для сортировки - это знать, нужно ли менять местами 2 строки в определенный момент, так? Вот CustomSort и вызывает переданную ему функцию, чтобы понять, какая из строк больше, и в зависимости от направления сортировки, принимает - или не принимает - решение об обмене их местами). Посмотри исходники CustomSort - станет понятно, что там происходит...
Первый фрагмент - это добавление в список текста запроса. То есть, start - позиция начала одного из тегов, finish - позиция амперсанда (&), процитированная тобой строка вычленит собственно сам запрос (без тега, без знака "=" и без амперсанда), и добавит его в список.
А второй процитированный фрагмент - это сама "соль" программы:
// для начала - печатаем все даты в файл. Причем для печати // каждой из них используется функция PrintData. // Это - потому, что я решил запросы оборачивать кавычками, // а с датами этого делать не нужно. Process (lstData, fout, @PrintData);
// Так. Даты напечатаны, теперь переходим к запросам. // Сначала отсортируем их по алфавиту: LstQueries.CustomSort(@MySort);
// Теперь уже отсортированный список выводим в тот же файл, // но уже используя свою функцию, чтоб запросы брались в кавычки Process (lstQueries, fout, @PrintQuery);
// Ну, и удаляем оба списка, разумеется... lstData.Free; lstQueries.Free;
Автор: Роман 1.06.2011 18:28
Спасибо теперь всё окончательно понятно Программа четко выполняет поставленную задачу