Создание своего рода «компонуемого» парсера для файлов журнала - PullRequest
1 голос
/ 24 октября 2009

Я запустил небольшой домашний проект для анализа файлов журнала для Team Fortress 2. В файлах журнала есть событие в каждой строке, например:

L 10/23/2009 - 21:03:43: "Mmm... Cycles!<67><STEAM_0:1:4779289><Red>" killed "monkey<77><STEAM_0:0:20001959><Blue>" with "sniperrifle" (customkill "headshot") (attacker_position "1848 813 94") (victim_position "1483 358 221")

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

Например:

my $name = qr/(.*)<(\d+)><(.*)><(Red|Blue)>/
my $kill = qr/"$name" killed "$name"/;

Это работает хорошо, но регулярное выражение теперь возвращает результаты, которые зависят от формата $name (ломая абстракцию, которую я пытаюсь достичь). Пример выше будет соответствовать как:

my ($name_1, $id_1, $steam_1, $team_1, $name_2, $id_2, $steam_2, $team_2)

Но я действительно ищу что-то вроде:

my ($player1, $player2)

Где $ player1 и $ player2 будут кортежами предыдущих данных. Я полагаю, что «убитому» событию не нужно знать точно об игроке, если у него есть информация для создания игрока, что и обеспечивают эти кортежи.

Извините, если это немного странно, но, надеюсь, вы сможете дать какой-нибудь совет!

Ответы [ 3 ]

4 голосов
/ 24 октября 2009

Мне кажется, я понимаю, о чем вы спрашиваете. Что вам нужно сделать, это изменить свою логику. Сначала вам нужно регулярное выражение, чтобы разделить строку на две части, затем вы извлекаете свои кортежи. Тогда вашему регулярному выражению не нужно знать об имени, и у вас просто есть два общих регулярных выражения игрока. Вот краткий пример:

#!/usr/bin/perl

use strict;
use Data::Dumper;

my $log = 'L 10/23/2009 - 21:03:43: "Mmm... Cycles!<67><STEAM_0:1:4779289><Red>" killed "monkey<77><STEAM_0:0:20001959><
Blue>" with "sniperrifle" (customkill "headshot") (attacker_position "1848 813 94") (victim_position "1483 358 221")';

my ($player1_string, $player2_string) = $log =~ m/(".*") killed (".*?")/;
my @player1 = $player1_string =~ m/(.*)<(\d+)><(.*)><(Red|Blue)>/;
my @player2 = $player2_string =~ m/(.*)<(\d+)><(.*)><(Red|Blue)>/;

print STDERR Dumper(\@player1, \@player2);

Надеюсь, это то, что вы искали.

1 голос
/ 26 октября 2009

Рассмотрите возможность записи подкласса Regexp :: Log .

1 голос
/ 24 октября 2009

Другой способ сделать это, но та же стратегия, что и в ответе dwp:

my @players = 
    map { [ /(.*)<(\d+)><(.*)><(Red|Blue)>/ ] }
    $log_text =~ /"([^\"]+)" killed "([^\"]+)"/
;

Ваши данные журнала содержат несколько элементов сбалансированного текста (в кавычках и в скобках), поэтому вы можете рассмотреть Text :: Balanced для частей этой работы или, возможно, подход анализа, а не прямую атаку с регулярным выражением , Последний может быть хрупким, если имена игроков могут содержать произвольный ввод, например.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...