【问题标题】:Searching Perl array with regex and return single capturing group only使用正则表达式搜索 Perl 数组并仅返回单个捕获组
【发布时间】:2021-11-26 04:01:40
【问题描述】:

我有一个 Perl 脚本,我在其中循环执行 Web 服务调用。服务器返回一个多值 HTTP 标头,我需要在每次调用后对其进行解析,其中包含我需要进行下一次调用的信息(如果它不返回标头,我想退出循环)。

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

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

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

  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)等都返回整个匹配,而不仅仅是我想要的单个捕获组。我想通过循环遍历数组并在循环体内进行匹配/解析来避免弄乱我的代码。

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

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    因此,您似乎想以某种模式捕获第一个元素,但只获取该模式。你希望它做得很好。事实上,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] 需要将正则表达式放在列表上下文中,以便它返回实际捕获。另一种方法是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-03-03)之前的模块版本没有firstres(别名first_result

    【讨论】:

    • 谢谢! first_result 听起来像我在想的,虽然我认为 map 可能是这里更优雅的解决方案(不敢相信我之前没有想到...)first_result 肯定会是我的东西'将在未来的脚本中使用。感谢您的帮助!
    • @flashbang 是的,这正是您的问题所要求的——在第一次匹配时停止,但从该元素中提取模式。使用map 处理输入很自然,不像grepfirst 所做的那样,这是我首先想到的。但我完全忽略了它,因为您特别想避免“额外”——代码、处理步骤等。map 必须始终传递整个列表。效率方面,与一些项目的列表几乎没有区别,但firstres 只是更充分地填补了账单。此外,虽然它可能非常有用,但很少被提及。
    • (另外,在效率方面,firstres 必须进行额外的簿记/测试才能在第一场比赛中退出,因此对于非常短的列表可能根本不会更快。)
    • 从外观上看,firstresmap 的代码看起来完全相同 - 您必须从块中显式返回您要返回的操作。唯一的区别是firstres 在第一场比赛中退出,所以事后没有什么可做的,所以实际上看起来更干净。我猜map 由于习惯和常用用法而显得更加优雅——它是一个众所周知且深受喜爱的内置函数,专门用于处理列表元素。
    【解决方案2】:

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

    您可以使用以下任何一种:

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

    使用first,它看起来像下面这样,这是相当愚蠢的:

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

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

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

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

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

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

    【讨论】:

    • 谢谢!就可读性而言,map 解决方案似乎是这种情况下最优雅的解决方案 - 因为列表永远不会超过几个元素,我不担心不必要地循环。 first_result 听起来像是我想要的,即使这不是我最终要在这里的:)
    【解决方案3】:

    以下代码 sn-p 正在寻找存储在变量$find 中的foo,找到的值存储在变量$found 中。

    
    my $find = 'foo';
    my $found;
    
    while( $response->header( 'X-Header' ) ) {
        if( /X-Header: .*?blah=($find)/ ) {
            $found = $1;
            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 = $1 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
    

    【讨论】:

    • 不幸的是,所有响应都会返回X-Header - 我需要检查是否提供了 foo 值以了解是否继续。
    • 是的,我正在使用 LWP - 抱歉,我想应该包括在内。由于此 API 不是公开的,因此我不认为深入了解细节会有很大帮助,但是 HTTP::Response 对象的 header 方法应该在任何地方都可以正常工作。
    • 请查看示例演示代码及其输出。我利用__DATA__ 块来模拟X-Header
    猜你喜欢
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    相关资源
    最近更新 更多