正则表达式可选择多次匹配模式

时间:2018-06-14 14:17:56

标签: php regex

我有一个字符串,我希望可选地匹配特定模式的次数。

我的字符串     
0.91 0.45 0.69 58 47 45 23 83 90 $595 NO IDL

45之后$595可能会有多达6个号码。我如何选择在该空间中查找重复的数字?

这是我到目前为止所拥有的:

/([\d.]+) ([\d.]+) ([\d.]+)? (\d+) (\d+) (\d+)  \$(\d+)/ig 

以下是一些预期输出的样本:

0.91 0.45 0.69 58 47 45 23 83 90 $595 NO IDL
output: array([0] => 0.91, 
              [1] => 0.45, 
              [2] => 0.69, 
              [3] => 58, 
              [4] => 47, 
              [5] => 45, 
              [6] => 23, 
              [7] => 83, 
              [8] => 90, 
              [9] => 595)

0.91 0.45 0.69 58 47 45 $595 NO IDL
output: array([0] => 0.91, 
              [1] => 0.45, 
              [2] => 0.69, 
              [3] => 58, 
              [4] => 47, 
              [5] => 45,  
              [5] => 595)

0.91 0.45 0.69 0.63 58 47 45 $595 NO IDL
output: Does not match the pattern because we only want 3 of the first items to contain decimals. 

这似乎将最后一个数字分成多个数字。无法弄清楚发生了什么。

我正在使用php preg_match方法,所以如果可能的话,不希望在结果数组中使用空元素。感谢。

3 个答案:

答案 0 :(得分:1)

您可以使用在字符串开头触发的正向前瞻来验证字符串,然后在验证成功后匹配从启动到货币值的所有数字:

'~(?:\G(?!^)|^(?=\d+\.\d+ \d+\.\d+ \d+(?:\.\d+)?(?: \d+)* \$\d))\s*\$?\K\d+(?:\.\d+)?~'

请参阅regex demo

<强>详情

  • (?:\G(?!^)|^(?=\d+\.\d+ \d+\.\d+ \d+(?:\.\d+)?(?: \d+)* \$\d)) - 上一个匹配的结尾(\G(?!^))或后面的字符串(^)的开头
    • \d+\.\d+
    • - 空格
    • \d+\.\d+
    • - 空格
    • \d+ - 1+位数
    • (?:\.\d+)? - 可选的小数部分
    • (?: \d+)* - 0+空格序列后跟1+位数
    • - space
    • \$\d - $和数字。
  • \s* - 0+ whitespaces
  • \$? - 可选的$字符
  • \K - 匹配重置运算符
  • \d+(?:\.\d+)? - 一个int / float数字(1+位后跟可选的.和1+位数序列。)

PHP demo

$strs = ['0.91 0.45 0.69 58 47 45 23 83 90 $595 NO IDL','0.91 0.45 0.69 58 47 45 $595 NO IDL','0.91 0.45 0.69 0.63 58 47 45 $595 NO IDL'];
$rx = '~(?:\G(?!^)|^(?=\d+\.\d+ \d+\.\d+ \d+(?:\.\d+)?(?: \d+)* \$\d))\s*\$?\K\d+(?:\.\d+)?~';
foreach ($strs as $s) {
    echo "$s:\n";
    if (preg_match_all($rx, $s, $matches)) {
        print_r($matches[0]);
        echo "---------\n";
    } else {
        echo "NO MATCH!!!\n---------\n";
    }

}

输出:

0.91 0.45 0.69 58 47 45 23 83 90 $595 NO IDL:
Array
(
    [0] => 0.91
    [1] => 0.45
    [2] => 0.69
    [3] => 58
    [4] => 47
    [5] => 45
    [6] => 23
    [7] => 83
    [8] => 90
    [9] => 595
)
---------
0.91 0.45 0.69 58 47 45 $595 NO IDL:
Array
(
    [0] => 0.91
    [1] => 0.45
    [2] => 0.69
    [3] => 58
    [4] => 47
    [5] => 45
    [6] => 595
)
---------
0.91 0.45 0.69 0.63 58 47 45 $595 NO IDL:
NO MATCH!!!
---------

答案 1 :(得分:0)

这应该会给你预期的结果:

/([\d\$.]+)/ig

答案 2 :(得分:0)

您可以重复这些数字,直到匹配45为第6个数字。

<强>解释

  • (?:\d+\.\d+)(?: \d+\.\d+){2}匹配开头的数字(带小数部分的数字)3次
  • (?: \d+){3}将数字与空格匹配3次。这将匹配到45
  • \s*匹配零个或多个空白字符
  • |
  • \G(?!^)使用否定前瞻断言上一场比赛结束时的位置断言不是字符串的开头
  • (\d+)\s捕获数字并匹配捕获组中的空白

(?:\d+\.\d+)(?: \d+\.\d+){2}(?: \d+){3}\s*|\G(?!^)(\d+)\s

Regex demo

例如,在45之后提取3位数的演示:

Demo