PHP preg_split使用分隔符作为数组键

时间:2018-09-18 08:48:23

标签: php arrays regex preg-match pcre

我需要用正则表达式定界符分割字符串,但需要使用定界符作为数组键。

这是一个示例字符串:

*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times

定界符是一个星号(*),后跟两个字母数字字符。 我使用以下正则表达式模式:/\*[A-Z0-9]{2}/

这是我的preg_split呼叫:

$attributes = preg_split('/\*[A-Z0-9]{2}/', $line);

这可行,但是我需要每个匹配的定界符作为关联数组中值的键。

我得到的是这样的:

$matches = [
        0 => 'the title',
        1 => 'the author',
        2 => 'other useless infos',
        3 => 'other useful infos',
        4 => 'some delimiters can be there multiple times'
    ];

它应该像这样:

$matches = [
        '*01' => 'the title',
        '*35' => 'the author',
        '*A7' => 'other useless infos',
        '*AE' => [
            'other useful infos',
            'some delimiters can be there multiple times',
        ],
    ];

有人对如何实现这一目标有任何建议吗?

3 个答案:

答案 0 :(得分:1)

使用PREG_SPLIT_DELIM_CAPTURE函数的preg_split标志还获取捕获的定界符(请参见documentation)。

所以在您的情况下:

# The -1 is the limit parameter (no limit)
$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);

现在,您将0中的元素$attributes作为第一个定界符之前的所有内容,然后将捕获的定界符和下一组分隔,从而可以像这样构建$matches数组(假设您不想保留第一组):

for($i=1; $i<sizeof($attributes)-1; $i+=2){
    $matches[$attributes[$i]] = $attributes[$i+1];
}

为了解决定界符多次出现的问题,您可以调整for循环内的行以检查此键是否已经存在,并在这种情况下创建一个数组。

编辑:如有必要,可以使用以下代码创建数组:

for($i=1; $i<sizeof($attributes)-1; $i+=2){
    $key = $attributes[$i];
    if(array_key_exists($key, $matches)){
        if(!is_array($matches[$key]){
            $matches[$key] = [$matches[$key]];
        }
        array_push($matches[$key], $attributes[$i+1]);
    } else {
        $matches[$attributes[$i]] = $attributes[$i+1];
    }
}

当然可以简化下游代码,特别是如果将所有值都放在(可能是单个元素)数组中。

答案 1 :(得分:1)

您可以将键匹配并捕获到组1中,并将所有下一个定界符之前的文本捕获到组2中,其中定界符与捕获的第一个定界符不同。然后,循环检查所有键和值,并使用定界符模式(一次或多次出现)分隔这些值。

正则表达式为

(\*[A-Z0-9]{2})(.*?)(?=(?!\1)\*[A-Z0-9]{2}|$)

请参见regex demo

详细信息

  • (\*[A-Z0-9]{2})-分隔符,第1组:一个*和两个大写字母或数字
  • (.*?)-值,组2:除换行符以外的任何0+个字符,并且尽可能少
  • (?=(?!\1)\*[A-Z0-9]{2}|$)-直到分隔符模式(\*[A-Z0-9]{2})不等于第1组((?!\1))或字符串结尾($)中捕获的文本。

请参见PHP demo

$re = '/(\*[A-Z0-9]{2})(.*?)(?=(?!\1)\*[A-Z0-9]{2}|$)/';
$str = '*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times';
$res = [];
if (preg_match_all($re, $str, $m, PREG_SET_ORDER, 0)) {
    foreach ($m as $kvp) {
        $tmp = preg_split('~\*[A-Z0-9]+~', $kvp[2]);
        if (count($tmp) > 1) {
            $res[$kvp[1]] = $tmp;
        } else {
            $res[$kvp[1]] = $kvp[2];
        }
    }
    print_r($res);
}

输出:

Array
(
    [*01] => the title
    [*35] => the author
    [*A7] => other useless infos
    [*AE] => Array
        (
            [0] => other useful infos
            [1] => some delimiters can be there multiple times
        )

)

答案 2 :(得分:0)

好的,我回答了关于如何处理多个相同定界符的问题。 感谢@ markus-ankenbrand的开始:

$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);
        $matches = [];
        for ($i = 1; $i < sizeof($attributes) - 1; $i += 2) {
            if (isset($matches[$attributes[$i]]) && is_array($matches[$attributes[$i]])) {
                $matches[$attributes[$i]][] = $attributes[$i + 1];
            } elseif (isset($matches[$attributes[$i]]) && !is_array($matches[$attributes[$i]])) {
                $currentValue = $matches[$attributes[$i]];
                $matches[$attributes[$i]] = [$currentValue];
                $matches[$attributes[$i]][] = $attributes[$i + 1];
            } else {
                $matches[$attributes[$i]] = $attributes[$i + 1];
            }
        }

胖的if / else语句看起来不太好,但是可以满足需要。