Использование регулярных выражений

advertisement
Использование регулярных выражений
Выполнила студентка группы 3087
Мишина А.С.
май, 2010 г.
1. Анализ log-файла
Постановка задачи
Для изучения регулярных выражений была написана программа на языке Perl.
Она предназначена для анализа лог-файлов, в которых фиксируются обращения
посетителей к веб-серверу Apache. Результатом анализа является уровень посещаемости
сервера и статистика кодов ошибок обработки запросов к серверу.
Данная задача является актуальной, т.к. для осуществления динамичного
развития и процветания сервера необходим систематический анализ статистики,
полученной путем правильной обработки файлов регистрации событий. Часто возникает
необходимость оценить популярность сайта, размещенного на сервере. Например,
разработчик сайта может проследить динамику посещений в зависимости от вносимых
изменений и модернизации, определить оптимальное направление развития сайта. С
другой стороны, количество посещений представляет интерес для пользователя и может
индицироваться на соответствующих веб-страницах. Статистика ошибок позволяет
выявить уязвимые места сайта и устранить их.
Описание алгоритма
Алгоритм предусматривает выполнение следующих операций.
На первом этапе выполняется чтение файла формата log (apache.log),
находящегося в директории, из которой запускается программа. При отсутствии файла
данного формата программа выдает соответствующее уведомление: «Error opening
apache.log»..
На втором этапе производится построчный анализ содержимого файла. Из
строки, содержащей информацию об IP-адресе клиента, времени поступления запроса,
содержимом запроса, коде ответа сервера клиенту, размере ответа клиенту (в байтах),
адресе страницы, откуда пришел посетитель и типе браузера посетителя, выделяется
соответственно дата и код ответа и выполняется подсчет количества посещений в день и
сбор информации о кодах ошибок и их количестве.
Заключительным этапом работы анализатора является визуализация результатов.
Результат выполнение программы сохраняется в файле out.txt. Если файл уже существует,
то его содержимое заменяется, при отсутствии файла он создается. Выходными данными
являются 2 таблицы, содержащие:
-результатом анализа является уровень посещаемости сервера;
-статистика кодов ответов (ошибок) обработки запросов к серверу и их расшифровки
Описание используемых регулярных выражений
Структура файла “apache.log”:
…
195.54.25.187 - - [05/Mar/2005:06:04:03 +0300] "GET /?what=ref HTTP/1.0" 200 6812
"http://www.yandex.ru/yandsearch?text=%EE%F2%E7%FB%E2+%EE+%F1%F2%F3%E4%E
5%ED%F2%E5&stype=www" "Mozilla/5.0 (Windows; U; Windows NT 5.0; ru-RU; rv:1.7.6)
Gecko/20050226 Firefox/1.0.1"
…
Считывание IP-адреса:
my @Ip = ($Text=~m/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/g);
Дата представлена в следующем виде: 05/Mar/2005:06:04:03
Для того чтобы в массив @dat сохранить все строки – даты, необходимо
использовать следующее регулярное выражение:
my @date = ($Text =~ m/[0-9]{2}\/[a-z]{3}\/[0-9]{4}\:[0-9]{2}\:[0-9]{2}\:[0-9]{2}/ig);
Затем считывается строка до кода ответа включительно, т.е. считываются любое
количество вхождений букв, цифр, знаков .,/,[,],+,-,?,/,= и т.д., пока не символ «кавычка»,
после которого будет идти три цифры подряд – код ошибки.
my @all = ($Text =~ m/[a-z0-9 \.\/\[\]\+\-\?\=\&\:_^(" )]*\" [0-9]{3}/ig);
Квантификатор (число в фигурных скобках) после символа, символьного класса
или группы определяет, сколько раз предшествующее выражение может встречаться.
Символ * обозначает любое количество вхождений символов.
В конце регулярных выражений использованы модификаторы:
i - поиск без учета регистра
g - глобальный поиск. Возвращает список всех найденных фрагментов (в контексте
массива).
Также использование деление считанных строк:
@str = split(/\/|:/, $date[$i]); - разделение строки-даты в массив, состоящий из дня,
месяца, года и т.д.
@tmp = split(/" /, $all[$i]); - разделение строки на нее саму без кода ответа и код
ответа;
Код программы
#открытие файлов apache.log и out.txt, находящихся в директории, из которой запускается
программа
#при отсутствии данных файлов программа выдает соответствующее уведомление
#файл apache.log открывается в режиме чтения, файл out.txt открывается в режиме записи
open (MYFILE, "<apache.log") || die "Error opening apache.log";
open (RESULT, ">out.txt");
$Filesize = -s MYFILE; #размер файла
read MYFILE, $Text, $Filesize;#чтение файла как одной большой строки
#поиск даты
my @date = ($Text =~ m/[0-9]{2}\/[a-z]{3}\/[0-9]{4}\:[0-9]{2}\:[0-9]{2}\:[0-9]{2}/ig);
#считывание строки до кода ответа включительно
my @all = ($Text =~ m/[a-z0-9 \.\/\[\]\+\-\?\=\&\:_^(" )]*\" [0-9]{3}/ig);
my @mas; #массив ссылок на массивы, содержащие день, месяц, год и количество
посещений
my @mistake; #массив ссылок на массивы, содержащие код ответа и его количество
my $maxindex=0;
#подсчет количества посещений в день
for ($i=0; $i<=$#date; $i++){
#разделение строки-даты в массив, состоящий из дня, месяца, года и т.д.
@str = split(/\/|:/, $date[$i]);
#сохранение нулевой строки-даты
if ($i==0){
for ($j=0; $j<3; $j++){
${$mas[$i]}[$j]=$str[$j];
}
${$mas[$i]}[$j]=1;
}
else{
#сравнение данной строки с предыдущей, если они совпадают, то значение
счетчика увеличивается на единицу
if ((${$mas[$#mas]}[2]==$str[2]) && (${$mas[$#mas]}[1]==$str[1]) &&
(${mas[$#mas]}[0]==$str[0])){
${$mas[$#mas]}[3]++;
}
#если строка не совпала, то создается новая ссылка
else {
$maxindex=$#mas+1;
for ($j=0; $j<3; $j++){
${$mas[$maxindex]}[$j]=$str[$j];
}
${$mas[$maxindex]}[$j]=1;
}
}
}
#суммирование по всему массиву mas количества посещений по одной дате
for ($j=0; $j<$#mas; $j++){
if (${$mas[$j]}[0]){
for ($k=$j+1; $k<=$#mas; $k++){
if (${$mas[$j]}[0]==${$mas[$k]}[0]){
${$mas[$j]}[3]=${$mas[$j]}[3]+${$mas[$k]}[3];
${$mas[$k]}[0]=0;
}
}
}
}
for ($i=0; $i<=$#all; $i++){
#разделение строки на нее саму без кода ответа и код ответа
@tmp = split(/" /, $all[$i]);
#сохранение нулевого кода ошибки
if ($i==0){
${$mistake[$i]}[0]=$tmp[1];
${$mistake[$i]}[1]=1;
}
else{
$k=0;
#сравнение кода ошибки с предыдущими и суммирование в случае совпадения
for ($j=0; $j<=$#mistake; $j++){
if ($tmp[1]==${$mistake[$j]}[0]){
${$mistake[$j]}[1]++;
$k=1;
break;
}
}
#если код ответа встречается впервые, то создается новая ссылка
if (!$k){
${$mistake[$#mistake+1]}[0]=$tmp[1];
${$mistake[$#mistake]}[1]=1;
}
}
}
select (RESULT); #настройка вывода для выходного файла
print "\nУровень посещаемости сайта\n\n";
#операция вывода верней части таблицы
$Begining=<<BEGINMARKER;
Число │ Месяц │ Год │ Количество посещений │
_______│_______│_______│______________________│
BEGINMARKER
print RESULT $Begining;
#выбор имени используемого формата
$~ = RESULT;
#присваивание значений переменным содержащимся в формате и вызов формата
функцией write
for ($i=0; $i<=$#mas; $i++){
if (${$mas[$i]}[0]){
$NumDate1=${$mas[$i]}[0];
$NumDate2=${$mas[$i]}[1];
$NumDate3=${$mas[$i]}[2];
$NumHit=${$mas[$i]}[3];
write;
}
}
#операция вывода закрытия таблицы
$Ending=<<ENDMARKER;
_______│_______│_______│______________________│
ENDMARKER
print $Ending;
#вывод периода, за который проводится статистика, т.е. минимальной и максимальной дат
print "\n\nСтатистика ответов(ошибок) за период ";
for ($i=0; $i<3; $i++){
print "${$mas[0]}[$i]";
if ($i<2) {print ".";}
}
print " - ";
for ($i=0; $i<3; $i++){
$k = $#mas;
while (${$mas[$k]}[0]==0) {$k--;}
print ${$mas[$k]}[$i];
if ($i<2) {print ".";}
}
#создание хеш-таблицы, содержащей коды ответов и их расшифровки
%Hash=( 200 => "Запрос обработан успешно",
304 => "Данный код ответа возвращается, если был запрос lf-Modified-Since, и
документ не изменялся с указанной даты",
… и т.д. осуществляется вывод в файл.
Результат работы программы
В результате обработки данного файла был получен следующий результат:
2. Нахождение любых чисел
Пример использования логических условий для нахождения любых чисел, в том
числе и в общепринятой математической записи. Для этого необходимо использовать
следующее регулярное выражение:
m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+]?\d*[,.]?\d+))%gxi;
[+-]? - есть ли в перед числом знак + или -. ? - если вообще есть что-то,
находящееся внутри впереди стоящего [...].
Regexp (?=\d|[\.,]\d)\d* логический оператор (?=B) требует, чтобы перед числом
было B. В данном случае B представляет из себя regex \d|[\.,]\d, это означает, что перед
каждым числом должно быть либо просто число, либо число, перед которым стоит либо
запятая, либо точка. Далее скобка закрывается и идет \d*. Для остальных цифр и нужен
квантификатор \d*, который значит любое количество цифр, в том числе и ноль, т.е. оно
работает и для числе вида .2 или ,2 Далее идет регулярное выражение ([\.,]\d*)? которое
говорит о том, есть ли вообще точка и запятая и число \d*.
Рассмотрим вторую половину регулярного выражения: ((\se|e|\s?\^)([+]?\d*[,\.]?)\d+)?. Эта строчка отвечает за поиск в строке $_ математических обозначений
степеней типа e201, E,20 и т.д. В конце стоит знак вопроса, т.е. если степенное
обозначение вообще существует. (\se|e|\s?\^) - в "компьютерной" записи вида 2 ^-,3 , т.е.
это регулярное выражение позволяет ставить или не ставить пробел при указании степени
и использовать знак ^ с пробелом перед ним (или без). Далее идет выражение ([+]?\d*[,\.]?), которое говорит о том, что степень может быть с + или -. Дальше идет цифра
\d, затем точка либо запятая. В конце используется \d+, т.е. должно быть хотя бы одно
число.
В конце использованы модификаторы: m%(что-то)%gxi.
i - поиск без учета регистра
g - глобальный поиск. Возвращает список всех найденных фрагментов (в
контексте массива)
x - разрешение разносить регулярное выражение на несколько строк
m - после и до символов новой строки
Таким образом, регулярным выражением
m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+]?\d*[,.]?\d+))%gxi;
предусмотрены числа степенного порядка, просто числа, числа со знаком,
нецелые числа вида ,3 (которое есть 0,3 или 0.3) и числа в "компьютерном"
представлении.
Пример программы
$_=qq~
1234
34 -4567
3456
-0.35e-0,2
56grf45
-.034 E20
-.034 e2,01 -,045 e-,23
-,034 e201 3e-.20
-,045 e-,23 e-0.88
4 E-0.20
22
E-21
-0.2 w
43
345
2 ^-,3
~;
print "$1\n" while
m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)
([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;
В результате работы программы в консольное приложение выводятся все числа.
Download