【问题标题】:print the matched word in perl regex在 perl 正则表达式中打印匹配的单词
【发布时间】:2017-07-14 18:34:15
【问题描述】:

我需要从 perl 中存储的行中打印所有匹配的字符串。我看过这方面的各种帖子 Print the matched string using perl Perl Regex - Print the matched value

我尝试先尝试打印第一个单词。但我得到一个构建错误

Use of uninitialized value $1 in concatenation (.) or string at rg.pl line 10.

我尝试过拆分和数组,它可以工作,但是在打印 $1 时,它会抛出错误。

我的代码在这里

#!/usr/bin/perl/
use warnings;
use strict;


#my $line = "At a far distance near the bar, was a parked car. Star were shining in the night. The boy in the car had scar and he was at war with his enemy. \n";

my $line = "At a far distance near the bar, was a parked car. \n";
if($line =~ /[a-z]ar/gi)
{       print "$1 \n";   }

$_ = $line;

我希望这段代码的输出是

far

并随后打印所有包含 ar 的单词,

far
near
bar
parked 
car

我什至尝试更改我的代码,如下所示,但没有奏效,同样的错误

if($line =~ /[a-z]ar/gi)  {
        my $match = $1;
        print "$match \n"; }

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    首先,您没有捕获任何东西,这就是$n 变量的填充方式。将要捕获的内容用括号括起来放入$1

    if ($line =~ /([a-z]ar)i/) { print "$1\n" }
    

    我已在此处删除了不需要的/g(并且可能会出现麻烦)。

    接下来,您的模式需要并捕获一个字母后跟文字 ar,不多不少。这不会捕获near,也不会捕获parked(它只会得到par)。它甚至不会匹配ar 开始 的单词,因为它要求ar 之前有一个字母。你需要使用quantifiers,告诉它匹配一个字母多少次。而且您还想找到所有匹配项。

    一种方法是通过提供列表context/g (global) modifier

    将它们全部收集起来
    my @words = $line =~ /([a-z]*ar[a-z]*)/gi;
    
    print "$_\n" for @words;
    

    [a-z]* 表示匹配一个字母零次或多次。所以一个可选的字母串。我们还在ar 之后添加了一个可选的字母字符串。 /g 使其在匹配后继续遍历字符串,以查找所有此类模式。在列表上下文中,返回匹配列表。

    或者,您可以像第一个示例一样在 标量上下文 中进行匹配,但在 while 循环中

    while ($line =~ /([a-z]*ar[a-z]*)/gi) { print "$1\n" }
    

    这里/g 做了一些不同的事情。它匹配一个模式一次并返回真,while 条件为真,我们打印。然后它返回并从它之前匹配的位置查找匹配 ...并继续这样做,直到没有更多匹配为止。

    这完全是复杂的行为。来自Regexp Quote-Like Operators in perlop

    /g 修饰符指定全局模式匹配——即在字符串中匹配尽可能多的次数。它的行为方式取决于上下文。在列表上下文中,它返回与正则表达式中的任何捕获括号匹配的子字符串列表。如果没有括号,则返回所有匹配字符串的列表,就好像整个模式都有括号一样。

    在标量上下文中,m//g 的每次执行都会找到下一个匹配项,如果匹配则返回 true,如果没有进一步匹配则返回 false。 [...]

    在“全局匹配”下以教程的方式in perlretut 更详细地阅读此内容。


    注意在标量上下文中使用/g修饰符

    我在上面的 while (/.../g) 中使用过它,这是一种非常常见的方式来跳过字符串中所有出现的模式,每次都让我们控制 while 正文。

    虽然这种用法是有意和惯用的,但在标量上下文中使用 /g 可能会在不在循环条件下时带来微妙的麻烦:此变量上带有 /g 的下一个正则表达式将从上一个匹配项继续,而不是从字符串的开头,可能会出乎意料。

    “下一个正则表达式”也可能只是同一个表达式——在我们的表达式恰好所在的某个更大循环的下一个循环中,这也适用于函数调用。考虑

    use warnings;
    use strict;
    use feature 'say';
    
    my $s = q(one two three);
    
    sub func { say $1 if $_[0] =~ /(\w+)/g };  # /g may be of great consequence!
    
    for (1..4) {
        # ... perhaps much, much later ...
        func($s);
    }
    

    这个循环打印行one,然后是two,然后是three,就是这样。这个(工作的)例子太简单了,以至于它是人造的,我希望它传达的/g在标量上下文中可能会令人惊讶。

    一方面,在if 条件下的正则表达式中看到/g 是完全错误的情况并不少见。

    【讨论】:

    • 能否请您多解释一下,这两者的工作方式有何不同? if ($line =~ /([a-z]ar)i/) { print "$1\n" } if($line =~ /[a-z]ar/gi) { print "$1 \n"; }
    • 一个是全局匹配(g),另一个不是。
    • @ShankhadeepMukerji 我添加了更完整的描述/评论。我会在审核时进行更多编辑。
    • 对于这样一个简单的问题来说,这大量矫枉过正。
    • @TimBiegeleisen 好吧,也许。我一直未能提供 minimal 答案。我的意思是,他们询问如何在全球范围内捕获。它可能是正则表达式中最常用的功能,而且很复杂。如果我只是在一行代码中给出直接答案,我觉得我会让他们人手不足,几乎被骗了。
    【解决方案2】:

    对于多个匹配项,请使用 while 循环。另外,我用括号将您要捕获的数量括起来,表示它是一个 capture 组。

    while ($line =~ /([a-z]*ar[a-z]*)/gi ) {
        print "$1 \n";
    }
    

    【讨论】:

    • 谢谢,这很有帮助。你在这里使用替代吗? s//// 没有得到那部分。
    • 这个/([a-z]+ar)/不会捕获parked,也不会匹配以ar开始的单词(它需要ar之前的字母)。跨度>
    猜你喜欢
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多