Как я могу разобрать строку в хеш, используя ключевые слова в Perl? - PullRequest
2 голосов
/ 25 мая 2010

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

Ключевые слова могут быть "first name: " и "last name: ". Теперь хочу разобрать:

"character first name: Han last name: Solo"

в

{ "first name: " => "Han ", "last name: " => "Solo" }

Конечно, порядок ключевых слов во входной строке не является фиксированным. Это также должно работать на:

"character last name: Solo first name: Han"

Я понимаю, что есть проблемы с пробелами и так далее. Я буду игнорировать их здесь.

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

Сплит почти отвечает всем требованиям. Единственная проблема заключается в том, что он возвращает массив, а не хеш, поэтому я не знаю, какое имя или фамилия.

Мой пример несколько вводит в заблуждение. Вот еще один:

my @keywords = ("marker 1", "marker 2", "marker 3");
my $rawString = "beginning marker 1 one un marker 2 two deux marker 3 three trois and the rest";
my %result;
# <grind result>
print Dumper(\%result);

напечатает:

$VAR1 = {
      'marker 2' => ' two deux ',
      'marker 3' => ' three trois and the rest',
      'marker 1' => ' one un '
    };

Ответы [ 6 ]

7 голосов
/ 25 мая 2010

Вот решение, использующее split (с режимом хранения разделителя), которое расширяется другими ключами:

use warnings;
use strict;

my $str = "character first name: Han last name: Solo";

my @keys = ('first name:', 'last name:');

my $regex = join '|' => @keys;

my ($prefix, %hash) = split /($regex)\s*/ => $str;

print "$_ $hash{$_}\n" for keys %hash;

который печатает:

last name: Solo
first name: Han 

Для обработки ключей, содержащих метасимволы регулярных выражений, замените строку my $regex = ... на:

 my $regex = join '|' => map {quotemeta} @keys;
3 голосов
/ 25 мая 2010

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

my %matches = $string =~ /($re):\s+(\S+)/g;

и покончим с этим.

В приведенном ниже сценарии рассматриваются возможные множественные вхождения.

#!/usr/bin/perl

use strict; use warnings;

use File::Slurp;
use Regex::PreSuf;

my $re = presuf( 'first name', 'last name' );

my $string = read_file \*DATA;
$string =~ s/\n+/ /g;

my %matches;

while ( $string =~ /($re):\s+(\S+)/g ) {
    push @{ $matches{ $1 } }, $2;
}

use Data::Dumper;
print Dumper \%matches;

__DATA__
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore character first name: Han last
name: Solo et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud character last name: Solo first name: Han exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum
character last name: Solo first name: Han dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum
2 голосов
/ 25 мая 2010

Это работает.

use 5.010;
use Regexp::Grammars;
my $parser = qr{
        (?:
            <[Name]>{2}
        )
        <rule: Name>
            ((?:fir|la)st name: \w+)
}x;

while (<DATA>) {
    /$parser/;
    use Data::Dumper; say Dumper $/{Name};
}

__DATA__
character first name: Han last name: Solo
character last name: Solo first name: Han

Выход:

$VAR1 = [
          ' first name: Han',
          ' last name: Solo'
        ];

$VAR1 = [
          ' last name: Solo',
          ' first name: Han'
        ];
2 голосов
/ 25 мая 2010
use strict;
use warnings;
use Data::Dump 'dump';   # dump allows you to see what %character 'looks' like

my %character;
my $nameTag = qr{(?:first|last) name:\s*};

# Use an array slice to populate the hash in one go
@character{ ($1, $3) } = ($2, $4) if $string =~ /($nameTag)(.+)($nameTag)(.+)/;

dump %character; # returns ("last name: ", "Solo", "first name: ", "Han ")
0 голосов
/ 25 мая 2010

Это возможно, ЕСЛИ:

1) Вы можете определить небольшой набор регулярных выражений, которые могут выбрать теги 2) Регулярное выражение для извлечения значения можно записать так, чтобы оно выбирало только значение и игнорировало следующие посторонние данные, если таковые имеются, между концом значения и началом следующего тега.

Вот пример того, как сделать это с очень простой строкой ввода. Это сеанс отладки:

  DB<14> $a = "a 13 b 55 c 45";
  DB<15> %$b = $a =~ /([abc])\s+(\d+)/g;
  DB<16> x $b
0  HASH(0x1080b5f0)
   'a' => 13
   'b' => 55
   'c' => 45
0 голосов
/ 25 мая 2010

Использовать текст :: ParseWords. Вероятно, он не делает всего, что вы хотите, но вы гораздо лучше опираетесь на него, чем пытаетесь решить всю проблему с нуля.

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