Извлечение текста между одинаковыми строками - PullRequest
2 голосов
/ 15 сентября 2011

Мне нужно извлечь строки текста между строками и заполнить их в файл Excel. Существует разница между количеством строк, но они начинаются с Комментарий к записи "idno" ... строка другого текста

__DATA__ (This is what my .txt file looks like)
Comment for the record "id1"
Attempt1 made on [time] outcome [outcome]
note 1

Comment for the record "id2"
Attempt1 made on [time] outcome [outcome]
note 1
Attempt2 made on [time] outcome [outcome]
note 2

Comment for the record "id3"
Attempt1 made on [time] outcome [outcome]
note 1
Attempt2 made on [time] outcome [outcome]
note 2
Attempt3 made on [time] outcome [outcome]
note 3
Attempt4 made on [time] outcome [outcome]
note 4

Хотите, чтобы это отображалось как

id1     Attempt1   Note1 [outcome]
id2     Attempt1   Note1 [outcome]
id2     Attempt2   Note2 [outcome]
id3     Attempt1   Note1 [outcome]
id3     Attempt2   Note2 [outcome]
id3     Attempt3   Note3 [outcome]
id3     Attempt4   Note4 [outcome]

Значение результата будет меняться и будет представлять собой 2-3-значный цифровой код.

Любая помощь будет принята с благодарностью. Я просмотрел этот сайт за последний день или два, но из-за своего ограниченного опыта я не смог найти что-то подходящее, и я, будучи довольно новым perl и shell, подумал, что будет лучше опубликовать его как вопрос.

С уважением, Ace

Ответы [ 6 ]

2 голосов
/ 16 сентября 2011

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

use strict;
use warnings;

# Paragraph mode: read the input file a paragraph/block at a time.
local $/ = "";

while (my $block = <>){
    # Convert the block to lines.
    my @lines = grep /\S/, split("\n", $block);

    # Parse the text, capturing needing items from @lines as we consume it.
    # Note also the technique of assigning regex captures directly to variables.
    my ($id) = shift(@lines) =~ /"(.+)"/;
    while (@lines){
        my ($attempt, $outcome) = shift(@lines) =~ /(Attempt\d+).+outcome (\d+)/;
        my $note = shift @lines;
        print join("\t", $id, $attempt, $note, $outcome), "\n";
    }
}
2 голосов
/ 15 сентября 2011

Использование GNU awk (для групп захвата регулярных выражений)

gawk '
    /^$/ {next}
    match($0, /Comment for the record "([^"]*)/, a) {id = a[1]; next}
    match($0, /(.+) made on .* outcome (.+)/, a) {att = a[1]; out = a[2]; next}
    {printf("%s\t%s\t%s\t%s\n", id, att, $0, out)}
'

или, переводя на Perl:

perl -lne '
    chomp;
    next if /^$/;
    if (/Comment for the record "([^"]*)/) {$id = $1; next;}
    if (/(.+) made on .* outcome (.+)/) {$att = $1; $out = $2; next;}
    print join("\t", $id, $att, $_, $out);
'
1 голос
/ 16 сентября 2011

Если я что-то упустил, это выглядит довольно просто:

  • Вы ищете строку, которая начинается с Comment. Это будет содержать ваш идентификатор.
  • Когда у вас есть идентификатор, у вас будет строка попытки, за которой следует строка примечания. Прочитайте попытку и строку после которой будет содержать примечание.
  • Когда вы переходите к следующему комментарию, вы промываете и повторяете.

У нас есть особая структура: каждый идентификатор будет иметь массив попыток . Каждая попытка будет содержать результат и примечание .

Я собираюсь использовать объектно-ориентированный Perl здесь. Я помещу все идентификаторы записи в список , называемый @dataList, каждый элемент в этом списке имеет тип Id.

Каждый тип Id будет состоять из массива Попыток , а каждая Попытка будет иметь Id , Время , Результат и Примечание .

#! /usr/bin/perl
# test.pl

use strict;
use warnings;
use feature qw(say);

########################################################################
# READ IN AND PARSE YOUR DATA
#

my @dataList;

my $record;
while (my $line = <DATA>) {
    chomp $line;
    if ($line =~ /^Comment for the record "(.*)"/) {
        my $id = $1;
        $record = Id->new($id);
        push @dataList, $record;
    }
    elsif ($line =~ /^(\S+)\s+made on\s(\S+)\soutcome\s(.*)/) {
        my $attemptId = $1;
        my $time = $2;
        my $outcome = $3;

        # Next line is the note

        chomp (my $note = <DATA>);
        my $attempt = Attempt->new($attemptId, $time, $outcome, $note);
        $record->PushAttempt($attempt);
    }
}

foreach my $id (@dataList) {
    foreach my $attempt ($id->Attempt) {
        print $id->Id . "\t";
        print $attempt->Id . "\t";
        print $attempt->Note . "\t";
        print $attempt->Outcome . "\n";
    }
}
#
########################################################################


########################################################################
# PACKAGE Id;
#
package Id;
use Carp;

sub new {
    my $class       = shift;
    my $id  = shift;

    my $self = {};

    bless $self, $class;

    $self->Id($id);

    return $self;
}

sub Id {
    my $self = shift;
    my $id   = shift;

    if (defined $id) {
        $self->{ID} = $id;
    }

    return $self->{ID};
}

sub PushAttempt {
    my $self        = shift;
    my $attempt = shift;

    if (not defined $attempt) {
        croak qq(Missing Attempt in call to Id->PushAttempt);
    }
    if (not exists ${$self}{ATTEMPT}) {
        $self->{ATTEMPT} = [];
    }
    push @{$self->{ATTEMPT}}, $attempt;

    return $attempt;
}

sub PopAttempt {
    my $self = shift;

    return pop @{$self->{ATTEMPT}};
}

sub Attempt {
    my $self = shift;
    return @{$self->{ATTEMPT}};
}


#
########################################################################

########################################################################
# PACKAGE Attempt
#
package Attempt;

sub new {
    my $class       = shift;
    my $id  = shift;
    my $time        = shift;
    my $note        = shift;
    my $outcome = shift;

    my $self = {};
    bless $self, $class;

    $self->Id($id);
    $self->Time($time);
    $self->Note($note);
    $self->Outcome($outcome);

    return $self;
}

sub Id {
    my $self = shift;
    my $id   = shift;


    if (defined $id) {
        $self->{ID} = $id;
    }

    return $self->{ID};
}

sub Time {
    my $self = shift;
    my $time = shift;

    if (defined $time) {
        $self->{TIME} = $time;
    }

    return $self->{TIME};
}

sub Note {
    my $self = shift;
    my $note = shift;

    if (defined $note) {
        $self->{NOTE} = $note;
    }

    return $self->{NOTE};
}

sub Outcome {
    my $self        = shift;
    my $outcome = shift;

    if (defined $outcome) {
        $self->{OUTCOME} = $outcome;
    }

    return $self->{OUTCOME};
}
#
########################################################################

package main;

__DATA__
Comment for the record "id1"
Attempt1 made on [time] outcome [outcome11]
note 11

Comment for the record "id2"
Attempt21 made on [time] outcome [outcome21]
note 21
Attempt22 made on [time] outcome [outcome22]
note 22

Comment for the record "id3"
Attempt31 made on [time] outcome [outcome31]
note 31
Attempt32 made on [time] outcome [outcome32]
note 32
Attempt33 made on [time] outcome [outcome33]
note 33
Attempt34 made on [time] outcome [outcome34]
note 34
1 голос
/ 15 сентября 2011

Я думаю, вы ищете что-то вроде этого. Это печать CSV, которая может быть открыта Excel

use strict;

local $/;

block(/(id\d+)/,$_) for split /\n\n/, <DATA>;

sub block {
  my ($id,$block) = @_;

  $block =~ s/.*?(?=Attempt)//s;

  print join(',', $id, /(Attempt\d+)/, /([^\n]+)$/, /outcome (\d+)/)."\n"
    for split /(?=Attempt)/, $block
  ;
}
0 голосов
/ 17 сентября 2011

awk oneliner на основе вашего примера.

kent$  awk 'NF==5{gsub(/\"/,"",$5);id=$5;next;} /^Attempt/{n=$1;gsub(/Attempt/,"Note",n);print id,$1,n,$6}' input                      
id1 Attempt1 Note1 [outcome]
id2 Attempt1 Note1 [outcome]
id2 Attempt2 Note2 [outcome]
id3 Attempt1 Note1 [outcome]
id3 Attempt2 Note2 [outcome]
id3 Attempt3 Note3 [outcome]
id3 Attempt4 Note4 [outcome]
0 голосов
/ 16 сентября 2011

Это, вероятно, не очень надежно, но вот забавная попытка с sed

sed -r -n 's/Comment for the record "([^"]+)"$/\1/;tgo;bnormal;:go {h;n;};:normal /^Attempt[0-9]/{s/(.+) made on .* outcome (.+)$/\1 \2/;G;s/\n/ /;s/(.+) (.+) (.+)/\3\t\1\t\2/;N;s/\t([^\t]+)\n(.+)/\t\2\t\1/;p;d;}' data.txt

Примечание: только GNU sed. В случае необходимости портативность достигается просто.

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