【问题标题】:Parsing a string to a hash将字符串解析为哈希
【发布时间】:2014-07-10 00:49:18
【问题描述】:

我有一个字符串:

<https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5>;
rel="next",
<https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5>;
rel="first",
<https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5>;
rel="last"

所以格式是

(<val>; rel="key")*

我想将其解析为具有以下格式的哈希:

next => https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5
first => https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5
last => https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5

在 Java 中,我会使用正则表达式模式来提取每个键 => 值对并将它们放入映射中。模式类似于:

<([^>]++)>;\s*rel="([^"]++)"

这会给我第二个匹配组中的键和第一个匹配组中的值。同样的方法是实现这一目标的最佳方法是 Perl,还是我可以做一些更时髦的事情?

附:我使用 Perl 而不是 Java 的原因是服务器没有 Java。

【问题讨论】:

  • 你所说的“snazzier”是什么意思? Perl 的正则表达式支持并不完全缺乏,在 Perl 中使用它们真的很常见。
  • @Mat 我知道 Perl 的正则表达式支持非常好。但我也知道 Perl 还有很多其他的字符串处理特性。我只是想知道是否有更内置的方法来处理双分隔列表到哈希。

标签: regex perl parsing


【解决方案1】:

我的第一个想法是用逗号分割字符串并使用三个子字符串,但在 while 循环中使用全局匹配可能会更好。

这应该做你想做的事。 (Perl 是迄今为止更好的文本处理工具!)

更新我刚刚意识到您选择的降价放弃了尖括号和换行符。这更合适吗?我假设它是一个多行字符串?

use strict;
use warnings;

my $str = <<'END';
<https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5>;
rel="next",
<https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5>;
rel="first",
<https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5>;
rel="last"
END

my %data;
while ($str =~ / < ([^<>]+) >; \s* rel="([^"]+)" (?:,\s*)? /xg) {
  $data{$2} = $1;
}

use Data::Dump;
dd \%data;

输出

{
  first => "https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5",
  last  => "https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5",
  next  => "https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5",
}

【讨论】:

  • 我最后选择了@Tom 的解决方案,因为它更加快速失败 - 如果在单个字符串中找不到该模式,那么您会收到错误消息。
  • @BoristheSpider:您可以选择最适合您需要的任何一个,但无论是 Tom 的代码还是我的代码都没有设置用于检测错误,因为您没有说您想要它。我的版本将在字符串中找到它可以找到的所有匹配项,并忽略任何格式错误的数据,而使用 Tom's,如果正则表达式根本不匹配并且 $k 设置为 undef,则会收到 Use of uninitialized value 警告。这不是正确的错误处理,并且有一些方法可以在没有任何警告的情况下给出无效的结果。
【解决方案2】:

您可以将split 字符串放在“,”上,然后使用map 创建哈希:

#!/usr/bin/env perl

use strict;
use warnings;

my $str = 'https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5; rel="next", https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5; rel="first", https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5; rel="last"';

my %hash = map { 
    my ($v, $k) = $_ =~ /\s*([^;]+);\s*rel="([^"]+)".*/; 
    $k => $v;
} split ',', $str;

foreach my $key (keys %hash) {
    print "$key => $hash{$key}\n"
}

输出:

first => https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5
next => https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5
last => https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5

更新

有了新的字符串,你可以这样做:

$str = q(<https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5>; rel="next", <https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5>; rel="first", <https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5>; rel="last");

my %hash = map { 
    my ($v, $k) = $_ =~ /<([^>]+)>;\s*rel="([^"]+)".*/; 
    $k => $v;
} split ',', $str;

得到相同的结果。

【讨论】:

  • 这也是一个有趣的方法,+1。我没有意识到您可以将列表强制转换为这样的哈希!
  • @Boris 很高兴你喜欢它 :) 我个人更喜欢这种方法(首先使用 split),因为我有点害怕复杂的正则表达式!
  • 我认为正则表达式(几乎)是相同的——区别在于对集合使用外部或内部迭代。你的也明确分裂,。然而,这有点失败,所以这是我最终使用的。
  • @Boris 感谢您接受我的回答,很高兴您发现它很有用。
【解决方案3】:
use strict;
use warnings;
my $string='https://gitlab.me.com/api/v3/projects/all?page=2&per_page=5; rel="next", https://gitlab.me.com/api/v3/projects/all?page=1&per_page=5; rel="first", https://gitlab.me.com/api/v3/projects/all?page=8&per_page=5; rel="last"';

my @array=split /,/, $string;
my %hash;

foreach(@array)
{
   if($_=~/(.*?);\s*rel\=\s*"([^"]+)"/)
   {
      $hash{$2}=$1;
   }
}

print "$_ =>  $hash{$_}\n" foreach(keys%hash);

【讨论】:

    猜你喜欢
    • 2019-04-04
    • 2013-02-04
    • 2012-02-22
    • 1970-01-01
    • 2014-07-24
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    相关资源
    最近更新 更多