Нетривиальный анализ строковой информации

Применение регулярных выражений для улучшения возможностей функции Split() не дает представления об их настоящей силе. В следующем примере регулярные выражения применяются для анализа файла журнала IIS. Файл журнала выглядит примерно так:

#Software: Microsoft Internet Information Server 4.0

#Version: 1.0

#Date: 1999-12-31 00:01:22

#Fields: time c-ip cs-method cs-uri-stem sc-status

00:01:31 157.56.214.169 GET /Default.htm 304

00:02:55 157.56.214.169 GET /docs/project/overview.htm 200

Следующая программа преобразует файл журнала в более удобный формат.

// Файл: logparse.cs

// Команда компиляции:

// csc logparse.cs /r:system.net.dll /r:system.text.regularexpressions.dll

using System;

using System.Net;

using System.IO;

using System.Text.RegularExpressions;

using System.Collections;

class Test

{

public static void Main(string[] args)

{

if (args.Length == 0) //Должен быть указан анализируемый файл

{

Console.WriteLine("No log file specified.");

}

else

ParseLogFile(args[0]);

}

public static void ParseLogFile(string filename)

{

if (!System.IO.File.FileExists(filename))

{

Console.WriteLine ("The file specified does not exist.");

}

else

{

FileStream f = new FileStream(filename, FileMode.Open);

StreamReader stream = new StreamReader(f);

string line;

line = stream.ReadLine(); // Строка заголовка

line = stream.ReadLine(); // Строка версии

line = stream.ReadLine(); // Строка даты

Regex regexDate= new Regex(@"\:\s(?<date>[^\s]+)\s");

Match match = regexDate.Match(line);

string date = "";

if (match.Length != 0)

date = match.Group("date").ToString();

line = stream.ReadLine(); // Строка Fields

Regex regexLine =

new Regex( // Последовательность цифр или :

@"(?<time>(\d|\:)+)\s" +

// Последовательность цифр или .

@"(?<ip>(\d|\.)+)\s" +

// Любая комбинация непробельных символов

@"(?<method>\S+)\s" +

// Любая комбинация непробельных символов

@"(?<uri>\S+)\s" +

// Последовательность цифр

@"(?<status>\d+)");

// Последовательно читать строки файла

// Сгенерировать описание для каждой строки

while ((line = stream.ReadLine()) != null)

{

//Console.WriteLine(line);

match = regexLine.Match(line);

if (match.Length != 0)

{

Console.WriteLine("date: {0} {1}", date,

match.Group("time"));

Console.WriteLine("IP Address: {0}",

match.Group("ip"));

Console.WriteLine("Method: {0}",

match.Group("method"));

Console.WriteLine("Status: {0}",

match.Group("status"));

Console.WriteLine("URI: {0}\n",

match.Group("uri"));

}

}

f.Close();

}

}

}

Общая структура программы выглядит знакомо. В этом примере использованы два регулярных выражения. Строка даты и регулярное выражение для ее анализа выглядят так:

#Date: 1999-12-31 00:01:22

\:\s(?<date>[^\s]+)\s

В программе регулярные выражения обычно записываются в виде строк-литералов, поскольку синтаксис регулярных выражений допускает использование служебного префикса \. Чтобы регулярное выражение было проще читать, его разбивают на отдельные элементы. Например, следующая запись совпадает в строке с двоеточием (:):

\:

Обратная косая черта (\) необходима из-за того, что в регулярных выражениях двоеточие обладает самостоятельным смыслом. Метасимвол \s соответствует одному пробельному символу (табуляции или пробелу).

В следующей части выражение ?<date> задает имя переменной, которой будут присвоены совпавшие символы для последующего извлечения:

(?<date>[^\s]+)

Выражение [^\s] называется символьной группой, а символ ^ означает «ни один из следующих символов». Таким образом, эта группа совпадает с любым непробельным символом. Наконец, символ + совпадает с одним или несколькими экземплярами предыдущей спецификации (то есть одним или несколькими непробельными символами). В приведенном примере эта часть выражения совпадает с последовательностью 1999-12-31.

Чтобы поиск давал более точный результат, можно воспользоваться метасимволом \d, соответствующим одной цифре. В этом случае все выражение принимает вид:

\:\s(?<date>\d\d\d\d-\d\d-\d\d)\s

Итак, мы разобрались с простым регулярным выражением. Для анализа строк файла журнала применяется более сложное регулярное выражение. Поскольку строки журнала имеют единый формат, в данном примере можно было воспользоваться функцией Split(), но тогда пример стал бы недостаточно показательным. Регулярное выражение состоит из следующих частей:

(?<time>(\d|\:)+)\s // последовательность цифр или : (время)

(?<ip>(\d|\.)+)\s // последовательность цифр или . (IP-адрес)

(?<method>\S+)\s // любые непробельные символы (метод HTTP)

(?<uri>\S+)\s // любые непробельные символы (URL)

(?<status>\d+) // последовательность цифр (статус)

Предлагаю ознакомиться с аналогичными статьями: