【问题标题】:Match all occurrences of a string匹配所有出现的字符串
【发布时间】:2012-07-18 13:17:55
【问题描述】:

我的搜索文字如下。

...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...

它包含很多行(实际上是一个javascript文件)但需要解析变量中的值 strings 即 aaa 、 bbb 、 ccc 、 ddd 、 eee

下面是Perl代码,底部还是用PHP

my $str = <<STR;
    ...
    ...
    var strings = ["aaa","bbb","ccc","ddd","eee"];
    ...
    ...
STR

my @matches = $str =~ /(?:\"(.+?)\",?)/g;
print "@matches";

我知道上面的脚本会匹配所有的瞬间,但它也会解析其他行中的字符串 ("xyz")。所以我需要检查字符串 var strings =

/var strings = \[(?:\"(.+?)\",?)/g

使用上面的正则表达式它会解析 aaa

/var strings = \[(?:\"(.+?)\",?)(?:\"(.+?)\",?)/g

使用上面的,将得到 aaabbb。因此,为了避免正则表达式重复,我使用了 '+' 量词,如下所示。

/var strings = \[(?:\"(.+?)\",?)+/g

但我只有 eee,所以 我的问题是为什么我只有在使用 '+' 量词时才得到 eee

更新 1:使用 PHP preg_match_all(这样做是为了获得更多关注 :-))

$str = <<<STR
    ...
    ...
    var strings = ["aaa","bbb","ccc","ddd","eee"];
    ...
    ...
STR;

preg_match_all("/var strings = \[(?:\"(.+?)\",?)+/",$str,$matches);
print_r($matches);

更新 2:为什么它匹配 eee ?因为 (?:\"(.+?)\",?)+ 的贪婪。通过消除贪婪/var strings = \[(?:\"(.+?)\",?)+?/ aaa 将被匹配。 但为什么只有一个结果?有没有什么方法可以通过使用单个正则表达式来实现?

【问题讨论】:

    标签: php regex perl preg-match-all


    【解决方案1】:

    您可能更喜欢这种解决方案,它首先使用/g 修饰符查找字符串var strings = [。这会将\G 设置为在[ 之后立即匹配下一个正则表达式,该正则表达式会查找所有紧随其后出现的双引号字符串,前面可能有逗号或空格。

    my @matches;
    
    if ($str =~ /var \s+ strings \s* = \s* \[ /gx) {
      @matches = $str =~ /\G [,\s]* "([^"]+)" /gx;
    }
    

    尽管使用了/g 修饰符,您的正则表达式/var strings = \[(?:\"(.+?)\",?)+/g 只匹配一次,因为var strings = [ 没有第二次出现。当匹配完成时,每个匹配返回捕获变量$1$2$3 等的值列表,并且/(?:"(.+?)",?)+/(无需转义双引号)将多个值捕获到$1 只留下最终值。您需要编写类似上述的内容,它只为每个匹配捕获一个值到$1

    【讨论】:

      【解决方案2】:

      这是一个单一的正则表达式解决方案:

      /(?:\bvar\s+strings\s*=\s*\[|\G,)\s*"([^"]*)"/g
      

      \G 是一个零宽度断言,它匹配上一个匹配结束的位置(如果是第一次匹配尝试,则匹配字符串的开头)。所以这就像:

      var\s+strings\s*=\s*[\s*"([^"]*)"
      

      ...第一次尝试,然后:

      ,\s*"([^"]*)"
      

      ...在那之后,但每场比赛都必须从最后一场比赛结束的地方开始。

      这是一个demo in PHP,但它也可以在 Perl 中使用。

      【讨论】:

      • 是的,它奏效了。谢谢... :-)。但是你能解释一下为什么它对/var strings = \[(?:\"(.+?)\",?)+?/ 不起作用吗?
      • 如果你从 Perl 版本中去掉g,或者调用preg_match 而不是preg_match_all,你会看到你得到相同的结果;你实际上只做一场比赛。在该匹配中,捕获组中的部分被多次应用,每次都会覆盖最后一次传递的结果。我正在进行多场比赛,并分别保存每场比赛的结果。
      【解决方案3】:

      因为+ 告诉它重复括号内的确切内容(?:"(.+?)",?) 一次或多次。所以它将匹配"eee" 字符串,然后查找该"eee" 字符串的重复,它没有找到。

      use YAPE::Regex::Explain;
      print YAPE::Regex::Explain->new(qr/var strings = \[(?:"(.+?)",?)+/)->explain();
      
      The regular expression:
      
      (?-imsx:var strings = \[(?:"(.+?)",?)+)
      
      matches as follows:
      
      NODE                     EXPLANATION
      ----------------------------------------------------------------------
      (?-imsx:                 group, but do not capture (case-sensitive)
                               (with ^ and $ matching normally) (with . not
                               matching \n) (matching whitespace and #
                               normally):
      ----------------------------------------------------------------------
        var strings =            'var strings = '
      ----------------------------------------------------------------------
        \[                       '['
      ----------------------------------------------------------------------
        (?:                      group, but do not capture (1 or more times
                                 (matching the most amount possible)):
      ----------------------------------------------------------------------
          "                        '"'
      ----------------------------------------------------------------------
          (                        group and capture to \1:
      ----------------------------------------------------------------------
            .+?                      any character except \n (1 or more
                                     times (matching the least amount
                                     possible))
      ----------------------------------------------------------------------
          )                        end of \1
      ----------------------------------------------------------------------
          "                        '"'
      ----------------------------------------------------------------------
          ,?                       ',' (optional (matching the most amount
                                   possible))
      ----------------------------------------------------------------------
        )+                       end of grouping
      ----------------------------------------------------------------------
      )                        end of grouping
      ----------------------------------------------------------------------
      

      一个更简单的例子是:

      my @m = ('abcd' =~ m/(\w)+/g);
      print "@m";
      

      仅打印d。这是由于:

      use YAPE::Regex::Explain;
      print YAPE::Regex::Explain->new(qr/(\w)+/)->explain();
      
      The regular expression:
      
      (?-imsx:(\w)+)
      
      matches as follows:
      
      NODE                     EXPLANATION
      ----------------------------------------------------------------------
      (?-imsx:                 group, but do not capture (case-sensitive)
                               (with ^ and $ matching normally) (with . not
                               matching \n) (matching whitespace and #
                               normally):
      ----------------------------------------------------------------------
        (                        group and capture to \1 (1 or more times
                                 (matching the most amount possible)):
      ----------------------------------------------------------------------
          \w                       word characters (a-z, A-Z, 0-9, _)
      ----------------------------------------------------------------------
        )+                       end of \1 (NOTE: because you are using a
                                 quantifier on this capture, only the LAST
                                 repetition of the captured pattern will be
                                 stored in \1)
      ----------------------------------------------------------------------
      )                        end of grouping
      ----------------------------------------------------------------------
      

      如果您在捕获组上使用量词,则只会使用最后一个实例。


      这是一种可行的方法:

      my $str = <<STR;
          ...
          ...
          var strings = ["aaa","bbb","ccc","ddd","eee"];
          ...
          ...
      STR
      
      my @matches;
      $str =~ m/var strings = \[(.+?)\]/; # get the array first
      my $jsarray = $1;
      @matches = $array =~ m/"(.+?)"/g; # and get the strings from that
      
      print "@matches";
      

      更新: 单行解决方案(虽然不是单个正则表达式)是:

      @matches = ($str =~ m/var strings = \[(.+?)\]/)[0] =~ m/"(.+?)"/g;
      

      但恕我直言,这是非常难以理解的。

      【讨论】:

      • 是的,这很酷。谢谢。但是有什么方法可以在单个正则表达式中完成。
      • @Jithin 你想要单个正则表达式的原因是什么?它很难阅读,而且所有小的正则表达式都比单个的工作速度更快
      • @simbabque 在您给出的简单示例中,它只匹配 d 因为贪婪。将您的表达式更改为m/(\w)+?/g 将匹配所有,即a b c d。但为什么它不适用于问题更新 2 中的组正则表达式?
      • @loldop 它不是关于单行正则表达式。 关于,为什么它不起作用? :-)
      • @Jithin: 但m/(\w)/g 也是如此——+ 是问题所在。我目前在perlre 找不到它。
      猜你喜欢
      • 1970-01-01
      • 2017-05-18
      • 2017-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-22
      相关资源
      最近更新 更多