Как сопоставить строку до и после сопоставления с образцом в Perl? - PullRequest
1 голос
/ 04 сентября 2010

Я сопоставляю шаблон и получаю строку соответствия, используя $.

Мне нужно напечатать соответствие строк перед определенным шаблоном и после определенного шаблона, например ::1004

line1
line2
line3
line4
line5

После того, как мой шаблон совпадет с line3, я хочу напечатать line2 и line4.

Как мне сделать сопоставление с образцом в Perl? Кто-нибудь может мне помочь?

Заранее спасибо

Senthil

Ответы [ 4 ]

3 голосов
/ 04 сентября 2010

Со всем файлом в скаляре напишите свой шаблон, чтобы он захватывал строки до и после line3. Модификатор /m особенно полезен:

Рассматривать строку как несколько строк. То есть измените ^ и $ с соответствия начала или конца строки на совпадение с началом или концом любой строки в любом месте строки.

Шаблоны ниже используют модификатор /x, который позволяет нам добавлять пробелы, чтобы они выглядели так, как они соответствуют.

Например:

#! /usr/bin/perl

my $data = do { local $/; <DATA> };

my $pattern = qr/ ^(.+\n)
                  ^line3\n
                  ^(.+\n)
                /mx;

if ($data =~ /$pattern/) {
  print $1, $2;
}
else {
  print "no match\n";
}

__DATA__
line1
line2
line3
line4
line5

Выход:

line2
line4

Помните, что $ - это утверждение: оно не потребляет никаких символов, поэтому вы должны сопоставлять символ новой строки с буквенным шаблоном \n.

Также обратите внимание, что приведенному выше шаблону не хватает общности. Он отлично работает для линии где-то посередине, но не удастся, если вы измените line3 на line1 или line5.

Для случая line1 можно сделать предыдущую строку необязательной с помощью квантификатора ?:

my $pattern = qr/ ^(.+\n)?
                  ^line1\n
                  ^(.+\n)
                /mx;

Как и ожидалось, получается

line2

Но пытаясь исправить то же самое для line5 дела

my $pattern = qr/ ^(.+\n)?
                  ^line5\n
                  ^(.+\n)?
                /mx;

дает

no match

Это потому, что после последнего перехода на новую строку в файле (после line5), ^ некуда найти, но шаблон меняется на

my $pattern = qr/ ^(.+\n)?
                  ^line5\n
                  (^.+\n)?
                /mx;

выходы

line4

Мы могли бы остановиться здесь, но асимметрия в шаблоне неприятна. Почему работал для одного случая, а не для другого? С line1, ^ соответствует началу $data, а затем ничего не соответствует (.+\n)?.

Помните: шаблоны, количественно определяемые с ? или * всегда , успешны, потому что они семантически совпадают с

  • ноль раз или один раз
  • ноль или более раз

соответственно и что угодно может совпадать с нулем раз:

$ perl -le 'print scalar "abc" =~ /(?!)*/'
1

Хотя я не могу вспомнить время, когда я когда-либо видел, чтобы оно использовалось таким образом, квантификатор {m,n}, где m равен нулю, например, ,

  • {0100}
  • {0}

всегда будет успешным, потому что m - это минимальное количество повторений. Квантор {0} является патологическим случаем, включенным для полноты.

Все, что нужно было показать, нам более или менее повезло с делом line1. ^ соответствует самому началу, ? -квантованный образец ничего не соответствует, а затем следующий ^ также соответствует самому началу $data.

Восстановление симметрии делает рисунок чище:

my $pattern = qr/ (^.+\n)?
                  ^line5\n
                  (^.+\n)?
                /mx;
3 голосов
/ 04 сентября 2010

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

#!/usr/bin/perl

use strict;
use warnings;

my $old;
while (my $line = <DATA>) {
    if ($line =~ /line3/) {
        print "$old$line", scalar <DATA>;
        last;
    }
    $old = $line;
}

__DATA__
line1
line2
line3
line4
line5

Если вам нужно более одной строки контекста, лучше использовать массив:

#!/usr/bin/perl

use strict;
use warnings;

my $context = shift || 3;
if ($context < 0) {
    $context = 0;
}

my @old;
while (my $line = <DATA>) {
    if ($line =~ /line6/) {
        print @old, $line;
        for (1 .. $context) {
            print scalar <DATA>;
        }
        last;
    }
    push @old, $line;
    #remove a line if we have more than we need
    if (@old > $context) {
        shift @old;
    }
}

__DATA__
line1
line2
line3
line4
line5
line6
line7
line8
line9
1 голос
/ 04 сентября 2010

Использование unix командной строки - это здорово, в таких случаях Perl обнимает его. попробуйте что-то вроде grep -A 1 или grep -B 1 это даст вам строку после / до

1 голос
/ 04 сентября 2010

Я понимаю, что вы спрашивали о Perl-решении, но в любом случае вот решение Unix grep:

grep -C 1 line3 file.txt

выводит:

line2
line3
line4

На странице grep

   -C NUM, --context=NUM
    Print  NUM lines of output context.  Places a line containing --
    between contiguous groups of matches.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...