如何使用Perl中的关键字将字符串解析为哈希?

时间:2010-05-25 14:06:57

标签: perl parsing keyword

我有一个字符串,其中不同的预定义关键字引入不同的数据。有没有办法通过巧妙地使用正则表达式或其他东西来做到这一点?这是一个例子:

关键字可以是"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 个答案:

答案 0 :(得分:7)

以下是使用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;

答案 1 :(得分:3)

以下循环遍历字符串一次以查找匹配(在规范化字符串之后)。避免循环的唯一方法是每个关键字只能在文本中出现一次。如果是这种情况,你可以写

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 :(得分:2)

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 ")

答案 3 :(得分:2)

这很有效。

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'
        ];

答案 4 :(得分:0)

这可能是IF:

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

答案 5 :(得分:-1)

使用Text :: ParseWords。它可能并不能完成您想要的所有功能,但是您可以更好地构建它,而不是尝试从头开始解决整个问题。