提问者:小点点

匹配字符串的所有出现次数


我的搜索文本如下。

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

它包含许多行(实际上是一个javascript文件),但需要解析变量字符串中的值,即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字符串=

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

使用上面的正则表达式,它将解析aaa。

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

使用上面,将获得aaa和bbb。所以为了避免正则表达式重复,我使用了''量词,如下所示。

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

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

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

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

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

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


共3个答案

匿名用户

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

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

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

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

…第一次尝试,然后:

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

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

这是一个PHP的演示,但它也可以在Perl中运行。

匿名用户

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

my @matches;

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

尽管使用了/g修饰符,您的regex/var字符串 = \[(?:\"(. ?) \",?) /g只匹配一次,因为没有第二次出现var字符串=[。每次匹配都返回一个捕获变量的值列表1美元2美元3美元等。当匹配完成时,/(?:"(. ?)",?) /(不需要转义双引号)将多个值捕获到1美元中,只留下最终值。您需要编写类似上述的内容,它仅将每个匹配项的单个值捕获到1美元中。

匿名用户

因为告诉它重复括号(?:"(. ?)",?) 中的内容一次或多次。所以它将匹配"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;

但这是高度不可读的imho。