仅使用正则表达式和 return 单个捕获组搜索 Perl 数组

Searching Perl array with regex and return single capturing group only

我有一个 Perl 脚本,我在其中循环执行 Web 服务调用。服务器 return 是一个多值 HTTP header,我需要在每次调用后使用我需要进行下一次调用的信息进行解析(如果它不 return header, 我想退出循环).

我只关心 header 中的一个值,我需要使用正则表达式从中获取信息。假设 header 是这样的,我只关心“foo”值:

X-Header: test-abc12345; blah=foo
X-Header: test-fgasjhgakg; blah=bar

我可以这样得到 header 个值:@values = $response->header( 'X-Header' );。但是如何快速检查 if

  1. 有一个foo值,并且
  2. 为下一次迭代解析并保存 foo 值?

理想情况下,我想做这样的事情:

my $value = 'default';

do {
  # (do HTTP request; use $value)
  @values = $response->header( 'X-Header' );
} while( $value = first { /(?:test-)([^;]+)(?:; blah=foo)/ } @values );

但是 grepfirst(来自 List::Util)等 return 整个 匹配 而不仅仅是我想要的单个捕获组。我想通过遍历数组和 matching/parsing 循环 body.

来避免弄乱我的代码

我想要的有可能吗?什么是最紧凑的编写方式?到目前为止,我所能想到的就是使用 lookarounds 和 \K 来丢弃我不关心的东西,但这不是超级可读的,并且会使正则表达式引擎执行很多不必要的步骤。

以下代码片段正在寻找存储在变量 $find 中的 foo,找到的值存储在变量 $found.


my $find = 'foo';
my $found;

while( $response->header( 'X-Header' ) ) {
    if( /X-Header: .*?blah=($find)/ ) {
        $found = ;
        last;
    }
}

say $found if $found;

示例演示代码

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $find = 'foo';
my $found;
my @header = <DATA>;

chomp(@header);

for ( @header ) {
    $found =  if /X-Header: .*?blah=($find)/;
    last if $found;
}

say Dumper(\@header);
say "Found: $found" if $found;

__DATA__
X-Header: test-abc12345; blah=foo
X-Header: test-fgasjhgakg; blah=bar

输出

$VAR1 = [
          'X-Header: test-abc12345; blah=foo',
          'X-Header: test-fgasjhgakg; blah=bar'
        ];

Found: foo

看来你想抓取第一个有特定模式的元素,但只获取模式。你希望它做得很好。实际上,firstgrep 只传递元素本身。

但是,List::MoreUtils::first_result 确实支持对其匹配项的处理

use List::MoreUtils 0.406 qw(first_result);

my @w = qw(a bit c dIT);  # get first "it" case-insensitive

my $res = first_result { ( /(it)/i )[0] } @w;

say $res // 'undef';  #--> it

需要 ( ... )[0] 将正则表达式放入列表上下文中,以便 returns 实际捕获。另一种方式是 firstres { my ($r) = /(it)/i; $r }。选择你的选择


对于题目中的数据

use warnings;
use strict;
use feature 'say';

use List::MoreUtils 0.406 qw(firstres);

my @data = ( 
    'X-Header: test-abc12345; blah=foo',
    'X-Header: test-fgasjhgakg; blah=bar'
);

if (my $r = firstres { ( /test-([^;]+);\s+blah=foo/ )[0] } @data) {
    say $r
}

打印 abc12345,在评论中澄清为寻求的结果。


0.406(2015 年 3 月 3 日)之前的模块版本没有 firstres(别名 first_result

first { ... } @values returns 值之一(或 undef)。

您可以使用其中之一:

my ($value) = map { /...(...).../ } @values;

my $value = ( map { /...(...).../ } @values ) ?  : undef;

my $value = ( map { /...(...).../ } @values )[0];

使用first,它看起来像下面这样,这很愚蠢:

my $value = first { 1 } map { /...(...).../ } @values;

但是,假设捕获不能为空字符串或字符串 0,List::MoreUtils 的 first_result 可用于避免不必要的匹配:

my $value = first_result { /...(...).../ ?  : undef } @values;

my $value = first_result { ( /...(...).../ )[0] } @values;

如果返回值可以为假(例如空字符串或 0),您可以使用

my $value = first_result { /...(...).../ ? $1 : undef } @values;
$value = $$value if $value;

first_result 方法在实践中不一定更快。