使用正则表达式提取数据(匹配前带有可选字符串)

Extract data with regex (with optional string before the match)

我有一个字符串数组。我正在尝试从每个字符串中提取括号 () 中的数据。问题是它不会从第一个元素中提取中间的数据,如果它前面没有其他内容。

这是指示 needed/captured 值的代码片段:

<?php

$data = [
    'aaa|45.85[u]52.22 - 43.75 - 36.5[d]25.75',
// #1^^^       #2^^^^^ #3^^^^^        #4^^^^^
    'bbb|238.4[u]345.45 - 24.1[d]13.85 - 56.4[d]56'
// #1^^^       #2^^^^^^        #3^^^^^        #4^^
];

$new = [];

foreach ($data as $element)
{
    preg_match("#^(.*?)\|[\w\[\.]+\]?(.*?) - [\w\[\.]+\]?(.*?) - [\w\[\.]+\]?(.*?)$#", $element, $match);
    
    $string = $match[1];
    $num1 = $match[2];
    $num2 = $match[3];
    $num3 = $match[4];

    $new[$string] = [
        'num1' => $num1,
        'num2' => $num2,
        'num3' => $num3,
    ];
}

print_r($new);

?>

上面的代码应该给我这样的结果:

$new = [
    'aaa' => [
        'num1' => '52.22',
        'num2' => '43.75',
        'num3' => '25.75',
    ],

    'bbb' => [
        'num1' => '345.45',
        'num2' => '13.85',
        'num3' => '56',
    ]
];

但它给了我这个:

$new = [
    'aaa' => [
        'num1' => '52.22',
        'num2' => '',
        'num3' => '25.75',
    ],

    'bbb' => [
        'num1' => '345.45',
        'num2' => '13.85',
        'num3' => '56',
    ]
];

查看此演示,了解您的第二个 [\w\[\.]+ 字符 class 是如何 over-matching 因为点和数字是贪婪匹配的,并且您的捕获组允许 zero-width 匹配。 https://regex101.com/r/zq6czS/1

只有两个示例字符串,很难自信地提出真正优化的模式,但我建议寻找贪婪量词的方法以提高性能。

  1. 在第一个竖线之前,收集所有不是竖线的字符 -- ([^|]+).
  2. 要在可选出现的“float then square-braced letter”之后捕获 non-whitespace 子字符串,再次使用否定字符 class -- (?:[^\]]+\])?(\S+)

#2 中的建议只重复了三遍;当然,由“space连字符 space”分隔。

代码:(Demo) (or with functionless assignments)

$data = [
    'aaa|45.85[u]52.22 - 43.75 - 36.5[d]25.75',
    'bbb|238.4[u]345.45 - 24.1[d]13.85 - 56.4[d]56'
];

$result = [];
foreach ($data as $element) {
    if (preg_match("#^([^|]+)\|(?:[^\]]+\])?(\S+) - (?:[^\]]+\])?(\S+) - (?:[^\]]+\])?(\S+)$#", $element, $matches)) {
        unset($matches[0]);
        $result[array_shift($matches)] = array_combine(['num1', 'num2', 'num3'], $matches);
    }
}
var_export($result);

一旦你有了你的 5 元素输出匹配数组,删除全字符串匹配 ($matches[0]),然后剥离新的第一个元素并将其用作第一级键,然后剩余的元素可以是添加到子数组。