Когда в выражении есть похожий шаблон, как извлечь последний экземпляр в Perl? - PullRequest
2 голосов
/ 12 мая 2011

Значение $s является динамическим. Мне нужно извлечь значения, которые появляются после последнего | между каждым [].

my $s = "[0|0|{A=145,B=2,C=12,D=18}|!][0|0|{A=167,B=2,C=67,D=17}|.1iit][196|0|{A=244,B=6,C=67,D=12}|10:48AM][204|0|{A=9,B=201,C=61,D=11}|Calculator][66|0|{A=145,B=450,C=49,D=14}|Coffee]";
my @parts = split(/\]/, $s);
foreach my $part (@parts)
{
    # Need to extract the values that occur after the last '|'
    # (for example: !, .1iit, 10:48AM, Calculator, Coffee)
    # and store each of the values separately in a hash     
}

Может ли кто-нибудь помочь мне в этом?

Спасибо

Ответы [ 6 ]

4 голосов
/ 12 мая 2011

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

#!/usr/bin/perl

use strict;
use warnings;

# Only needed for Dumper
use Data::Dumper;

my $s = "[0|0|{A=145,B=2,C=12,D=18}|!][0|0|{A=167,B=2,C=67,D=17}|.1iit][196|0|{A=244,B=6,C=67,D=12}|10:48AM][204|0|{A=9,B=201,C=61,D=11}|Calculator][66|0|{A=145,B=450,C=49,D=14}|Coffee]";

# Extract each group between []
# Then transform each group into an array reference by splitting on |
my @groups = map { [ split /\|/ ] } ($s =~ /\[([^\]]*)\]/g);

# Inspect the data structure
print Dumper \@groups;

# Print only the last element of each sub-array
print "$_\n" for map {$_->[-1]} @groups;

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

Редактировать: так как мне показалось интересным, я закончил создавать эти хэш-ссылки, вот код, который заменит строку my @groups:

my @groups = map { [ map { /\{([^\}]*)\}/ ? { split /(?:=|,)/, $1 } : $_ } (split /\|/) ] } ($s =~ /\[([^\]]*)\]/g);

или более правильно прокомментированные (map команды читаются сзади, поэтому комментарии начинаются снизу и следуют по номерам, комментарии как #/N сочетаются с такими как #N)

my @groups = map { #/1
  [ #/2
    map { #/3 
      /\{([^\}]*)\}/ #4 ... and if any element (separated by pipes in #3) 
                     #      is surrounded by curly braces
        ? { #5 ... then return a hash ref
            split /(?:=|,)/, $1 #6 ... whose elements are given 
                                #      pairwise between '=' or ',' signs
          } #/5
        : $_ #7 ... otherwise (from 'if' in #4 ) return the element as is
    } (split /\|/) #3 ... where each element is separated by pipes (i.e. |)
  ] #2 ... return an array ref
} ($s =~ /\[([^\]]*)\]/g); #1 For each element between sqr braces (i.e. [])
2 голосов
/ 12 мая 2011

Поскольку вы настаиваете на регулярном выражении:

@matches = $s =~ /\|([^|]+?)]/g

Использование / g сбросит все совпадения в массив @ совпадения

2 голосов
/ 12 мая 2011

Общий способ:

@subparts = split /\|/, $part;
$tail = $subparts[$#subparts];

Если вам когда-либо нужна последняя часть отдельно:

$part =~ /([^\|]*)$/ and $tail = $1;
2 голосов
/ 12 мая 2011

Вам действительно не нужно регулярное выражение ... просто используйте split().Результаты сохраняются в %results

my $s = "[0|0|{A=145,B=2,C=12,D=18}|!][0|0|{A=167,B=2,C=67,D=17}|.1iit][196|0|{A=244,B=6,C=67,D=12}|10:48AM][204|0|{A=9,B=201,C=61,D=11}|Calculator][66|0|{A=145,B=450,C=49,D=14}|Coffee]";
foreach my $part (split(/\]/, $s))
{
    @pieces = split(/\|/, $part);
    $results{$pieces[-1]} = $pieces[-1];
}
2 голосов
/ 12 мая 2011
my ($value) = $part =~ m/[^|]\|(.+)$/;

print "$part => $value\n";

и другим способом:

my $s =
"[0|0|{A=145,B=2,C=12,D=18}|!][0|0|{A=167,B=2,C=67,D=17}|.1iit][196|0|{A=244,B=6,C=67,D=12}|10:48AM][204|0|{A=9,B=201,C=61,D=11}|Calculator][66|0|{A=145,B=450,C=49,D=14}|Coffee]";
my @parts = $s =~ m/\|([^|]+)]/g;

print join( "\n", @parts );
1 голос
/ 12 мая 2011

При регулярных выражениях, когда вы думаете: «Я хочу последнее из», вы должны немедленно думать о шаблоне .*, потому что жадность регулярных выражений делает именно то, что вы хотите.

Например, /^(.*)a(.*)$/ соответствует "abababab" в

  • ababab в $1
  • a соответствует литералу в шаблоне
  • b в $2

Давайте подумаем над процессом матча. Представьте .* как Августа Глупа .

Август: Ausgezeichnet! Якорь ^ означает, что я начинаю с самого начала. Оттуда я буду есть все конфеты!

Вилли Вонка: Но, мой дорогой Август, ты должен поделиться с другими детьми.

Август: Хорошо, я получаю "abababa", а они получают "b". Счастливы?

Вилли Вонка: Но следующий ребенок в очереди не любит b конфеты.

Август: Тогда я оставлю "ababab" для себя и оставлю "ab" для остальных.

В этот момент у Августа большая кучка, скромный маленький Чарли Бакет получает свой сингл a, а Верука Солт - хотя и хмурится из-за скудного количества - теперь получает хоть что-то.

Другими словами, $2 содержит все после последнего a. Чтобы быть привередливыми, якоря ^ и $ являются избыточными, но мне нравится сохранять их для дополнительного акцента.

Приведя это в действие, вы могли бы написать

#! /usr/bin/env perl

use strict;
use warnings;

sub last_fields {
  local($_) = @_;

  my @last;
  push @last, $1 =~ /^.*\|(.+)$/ ? $1 : undef
    while /\[(.*?)\]/g;

  wantarray ? @last : \@last;
}

Внешний while разбивает строку на [...] кусков и предполагает, что правая квадратная скобка не может встречаться внутри блока. В каждом блоке мы используем /^.*\|(.+)$/ для захвата в $1 всего, что находится после последней трубы.

Тестирование на вашем примере выглядит как

my $s = "[0|0|{A=145,B=2,C=12,D=18}|!]" .
        "[0|0|{A=167,B=2,C=67,D=17}|.1iit]" .
        "[196|0|{A=244,B=6,C=67,D=12}|10:48AM]" .
        "[204|0|{A=9,B=201,C=61,D=11}|Calculator]" .
        "[66|0|{A=145,B=450,C=49,D=14}|Coffee]";

use Test::More tests => 6;
my @lasts = last_fields $s;

# yes, is_deeply could do this in a single call,
# but it's laid out explicitly here for expository benefit
is $lasts[0], "!";
is $lasts[1], ".1iit";
is $lasts[2], "10:48AM";
is $lasts[3], "Calculator";
is $lasts[4], "Coffee";
is scalar @lasts, 5;

Все тесты пройдены:

$ ./match-last-of 
1..6
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6

Вывод prove приятнее. Запустите его сами, чтобы увидеть цветовую кодировку.

$ prove ./match-last-of 
./match-last-of .. ok   
All tests successful.
Files=1, Tests=6,  0 wallclock secs ( 0.02 usr  0.01 sys +  0.02 cusr  0.00 csys =  0.05 CPU)
Result: PASS
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...