Как мне разобраться в следующем журнале? - PullRequest
2 голосов
/ 10 июня 2009

Мне нужно проанализировать журнал в следующем формате:

===== Item 5483/14800  =====
This is the item title
Info: some note
===== Item 5483/14800 (Update 1/3) =====
This is the item title
Info: some other note
===== Item 5483/14800 (Update 2/3) =====
This is the item title
Info: some more notes
===== Item 5483/14800 (Update 3/3) =====
This is the item title
Info: some other note
Test finished. Result Foo. Time 12 secunds.
Stats: CPU 0.5 MEM 5.3
===== Item 5484/14800  =====
This is this items title
Info: some note
Test finished. Result Bar. Time 4 secunds.
Stats: CPU 0.9 MEM 4.7
===== Item 5485/14800  =====
This is the title of this item
Info: some note
Test finished. Result FooBar. Time 7 secunds.
Stats: CPU 2.5 MEM 2.8

Мне нужно только извлечь заголовок каждого элемента (следующая строка после ===== Item 5484/14800 =====) и результат.
Поэтому мне нужно сохранить только строку с заголовком элемента и результатом для этого заголовка и отбросить все остальное.
Проблема заключается в том, что иногда у элемента есть заметки (максимум 3), а иногда результат отображается без дополнительных заметок, поэтому это сложно.
Любая помощь будет оценена. Я делаю парсер в python, но мне не нужен сам код, а некоторые указывают на то, как я могу добиться этого?

Л.Э .: Результат, который я ищу, это отказаться от всего остального и получить что-то вроде:

('This is the item title','Foo')
then
('This is this items title','Bar')

Ответы [ 9 ]

5 голосов
/ 10 июня 2009
1) Loop through every line in the log

    a)If line matches appropriate Regex:

      Display/Store Next Line as the item title.
      Look for the next line containing "Result 
      XXXX." and parse out that result for 
      including in the result set.

РЕДАКТИРОВАТЬ: добавлено немного больше, теперь я вижу результат, который вы ищете.

5 голосов
/ 11 июня 2009

Я знаю, что вы не спрашивали о реальном коде, но это слишком хорошая возможность для генераторного текстового мункера пропустить:

# data is a multiline string containing your log, but this
# function could be easily rewritten to accept a file handle.
def get_stats(data):

   title = ""
   grab_title = False

   for line in data.split('\n'):
      if line.startswith("====="):
         grab_title = True
      elif grab_title:
         grab_title = False
         title = line
      elif line.startswith("Test finished."):
         start = line.index("Result") + 7
         end   = line.index("Time")   - 2
         yield (title, line[start:end])


for d in get_stats(data):
   print d


# Returns:
# ('This is the item title', 'Foo')
# ('This is this items title', 'Bar')
# ('This is the title of this item', 'FooBar')

Надеюсь, это достаточно просто. Спросите, есть ли у вас вопросы о том, как именно это работает.

1 голос
/ 12 июня 2009

Это своего рода продолжение решения Мачейки (см. Комментарии там). Если данные находятся в файле daniels.log, то мы можем просмотреть его по элементам с помощью itertools.groupby и применить многострочное регулярное выражение к каждому элементу. Это должно хорошо масштабироваться.

import itertools, re

p = re.compile("Result ([^.]*)\.", re.MULTILINE)
for sep, item in itertools.groupby(file('daniels.log'),
                                   lambda x: x.startswith('===== Item ')):
    if not sep:
        title = item.next().strip()
        m = p.search(''.join(item))
        if m:
            print (title, m.group(1))
1 голос
/ 11 июня 2009

Регулярное выражение с сопоставлением групп, кажется, делает работу в python:

import re

data = """===== Item 5483/14800  =====
This is the item title
Info: some note
===== Item 5483/14800 (Update 1/3) =====
This is the item title
Info: some other note
===== Item 5483/14800 (Update 2/3) =====
This is the item title
Info: some more notes
===== Item 5483/14800 (Update 3/3) =====
This is the item title
Info: some other note
Test finished. Result Foo. Time 12 secunds.
Stats: CPU 0.5 MEM 5.3
===== Item 5484/14800  =====
This is this items title
Info: some note
Test finished. Result Bar. Time 4 secunds.
Stats: CPU 0.9 MEM 4.7
===== Item 5485/14800  =====
This is the title of this item
Info: some note
Test finished. Result FooBar. Time 7 secunds.
Stats: CPU 2.5 MEM 2.8"""


p =  re.compile("^=====[^=]*=====\n(.*)$\nInfo: .*\n.*Result ([^\.]*)\.",
                re.MULTILINE)
for m in re.finditer(p, data):
     print "title:", m.group(1), "result:", m.group(2)er code here

Если вам нужна дополнительная информация о регулярных выражениях, проверьте: python docs .

1 голос
/ 10 июня 2009

Я бы порекомендовал запустить цикл, который ищет "===" в строке. Пусть этот ключ вас к заголовку, который является следующей строкой. Установите флаг, который ищет результаты, и если вы не найдете результаты до того, как нажмете следующее «===», не говорите результатов. Иначе, зарегистрируйте результаты с заголовком. Сбросьте свой флаг и повторите. Вы также можете сохранить результаты с заголовком в словаре, просто сохраните «Нет результатов», если вы не нашли результатов между заголовком и следующей строкой «===».

Это выглядит довольно просто сделать на основе результатов.

1 голос
/ 10 июня 2009

Может быть что-то вроде (log.log это ваш файл):

def doOutput(s): # process or store data
    print s

s=''
for line in open('log.log').readlines():
    if line.startswith('====='):
        if len(s):
            doOutput(s)
            s=''
    else:
        s+=line
if len(s):
    doOutput(s)
0 голосов
/ 10 июня 2009

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

#!/usr/bin/perl -w
#
# $Id$
#

use strict;
use warnings;

my @ITEMS;
my $item;
my $state = 0;

open(FD, "< data.txt") or die "Failed to open file.";
while (my $line = <FD>) {
    $line =~ s/(\r|\n)//g;
    if ($line =~ /^===== Item (\d+)\/\d+/) {
        my $item_number = $1;
        if ($item) {
            # Just to make sure we don't have two lines that seems to be a headline in a row.
            # If we have an item but haven't set the title it means that there are two in a row that matches.
            die "Something seems to be wrong, better safe than sorry. Line $. : $line\n" if (not $item->{title});
            # If we have a new item number add previuos item and create a new.
            if ($item_number != $item->{item_number}) {
                push(@ITEMS, $item);
                $item = {};
                $item->{item_number} = $item_number;
            }
        } else {
            # First entry, don't have an item.
            $item = {}; # Create new item.
            $item->{item_number} = $item_number;
        }
        $state = 1;
    } elsif ($state == 1) {
        die "Data must start with a headline." if (not $item);
        # If we already have a title make sure it matches.
        if ($item->{title}) {
            if ($item->{title} ne $line) {
                die "Title doesn't match for item " . $item->{item_number} . ", line $. : $line\n";
            }
        } else {
            $item->{title} = $line;
        }
        $state++;
    } elsif (($state == 2) && ($line =~ /^Info:/)) {
        # Just make sure that for state 2 we have a line that match Info.
        $state++;
    } elsif (($state == 3) && ($line =~ /^Test finished\. Result ([^.]+)\. Time \d+ secunds{0,1}\.$/)) {
        $item->{status} = $1;
        $state++;
    } elsif (($state == 4) && ($line =~ /^Stats:/)) {
        $state++; # After Stats we must have a new item or we should fail.
    } else {
        die "Invalid data, line $.: $line\n";
    }
}
# Need to take care of the last item too.
push(@ITEMS, $item) if ($item);
close FD;

# Loop our items and print the info we stored.
for $item (@ITEMS) {
    print $item->{item_number} . " (" . $item->{status} . ") " . $item->{title} . "\n";
}
0 голосов
/ 10 июня 2009

Вы можете попробовать что-то вроде этого (в псевдокоде c-like, так как я не знаю python):

string line=getline();
regex boundary="^==== [^=]+ ====$";
regex info="^Info: (.*)$";
regex test_data="Test ([^.]*)\. Result ([^.]*)\. Time ([^.]*)\.$";
regex stats="Stats: (.*)$";
while(!eof())
{
  // sanity check
  test line against boundary, if they don't match, throw excetion

  string title=getline();

  while(1)
  {  
    // end the loop if we finished the data
    if(eof()) break;

    line=getline();
    test line against boundary, if they match, break
    test line against info, if they match, load the first matched group into "info"
    test line against test_data, if they match, load the first matched group into "test_result", load the 2nd matched group into "result", load the 3rd matched group into "time"
    test line against stats, if they match, load the first matched group into "statistics"
  }

  // at this point you can use the variables set above to do whatever with a line
  // for example, you want to use title and, if set, test_result/result/time.

}
0 голосов
/ 10 июня 2009

Синтаксический анализ не выполняется с помощью регулярных выражений. Если у вас достаточно хорошо структурированный текст (который выглядит так, как вы), вы можете использовать более быстрое тестирование (например, line.startswith () или подобное) Список словарей кажется подходящим типом данных для таких пар ключ-значение. Не уверен, что еще тебе сказать. Это кажется довольно тривиальным.


ОК, поэтому способ регулярного выражения оказался более подходящим в этом случае:

import re
re.findall("=\n(.*)\n", s)

быстрее, чем список списков

[item.split('\n', 1)[0] for item in s.split('=\n')]

Вот что я получил:

>>> len(s)
337000000
>>> test(get1, s) #list comprehensions
0:00:04.923529
>>> test(get2, s) #re.findall()
0:00:02.737103

Извлеченный урок.

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