如何使用 preg_match 或 preg_match_all 仅返回命名组?

23

例子:

$string = "This is some text written on 2010-07-18.";
preg_match('|(?<date>\d\d\d\d-\d\d-\d\d)|i', $string, $arr_result);
print_r($arr_result);

返回:

Array
(
    [0] => 2010-07-18
    [date] => 2010-07-18
    [1] => 2010-07-18
)

但我希望它是:

Array
(
    [date] => 2010-07-18
)

在PHP的PDO对象中,有一个选项可以通过删除重复的编号值来过滤数据库中的结果:PDO::FETCH_ASSOC。但是我还没有看到类似的修改器用于PHP中的PCRE函数。

您可以使用T-Regx并使用namedGroups()方法。 - Danon
9个回答

20

如何使用preg_match或preg_match_all仅返回命名分组?

目前(PHP7)不可能直接实现。您将始终获得一个混合类型的数组,其中包含数字和命名键。

让我们引用PHP手册(http://php.net/manual/en/regexp.reference.subpatterns.php):

此子模式将按其常规数字位置和名称在匹配的数组中进行索引。


以下代码段可能有助于解决问题:

1. 通过对数组键使用is_string检查来过滤数组 (适用于PHP5.6+)

$array_filtered = array_filter($array, "is_string", ARRAY_FILTER_USE_KEY);

2. 遍历数组元素,如果其键为整数,则将其删除(适用于所有 PHP 版本)

/**
 * @param array $array
 * @return array
 */
function dropNumericKeys(array $array)
{
    foreach ($array as $key => $value) {
        if (is_int($key)) {
            unset($array[$key]);
        }
    }
    return $array;
}

这是一个简单的PHP函数,名为dropNumericKeys()。它用于在使用命名组进行匹配的preg_match*()运行后,对匹配数组进行后处理。该函数接受一个$array参数。它遍历数组并删除/取消所有整数类型的键,保留字符串类型的键不变。最后,该函数返回仅具有命名键的数组。

注意:此函数用于PHP向下兼容性。它适用于所有版本。而array_filter解决方案则依赖于常量ARRAY_FILTER_USE_KEY,该常量仅在PHP5.6+中可用。请参见http://php.net/manual/de/array.constants.php#constant.array-filter-use-key


如果你有一个像 #1 这样简单的解决方案,为什么还要定义一个函数呢? - Meisner
2
该函数用于 PHP 的向下兼容性。它适用于所有版本。array_filter 解决方案依赖于常量 ARRAY_FILTER_USE_KEY,该常量仅在 PHP5.6+ 上可用。请参阅 http://php.net/manual/de/array.constants.php#constant.array-filter-use-key。 - Jens A. Koch
第三个选项是使用array_intersect_key函数与名称列表一起使用。 - Brad Kent

14

preg_match目前没有任何标志或选项可以仅返回命名匹配。因此,您想要的直接实现不可能。但是,您可以从匹配数组中删除所有非匹配键的项,然后就可以得到您想要的结果:

$matches = array_intersect_key($matches, array_flip(array('name', 'likes')));

4

我认为你不能让preg_*做到这一点,但你可以通过简单的循环来实现。但我不明白为什么这些元素会造成问题。


在小数组中,这不会造成问题,但在具有许多模式的非常大的数组中可能存在高内存需求的问题。其次,这样看起来更清晰,而且会少一行代码(使用这个不存在的修饰符),因此出错的可能性更小。 - rsk82
恰恰相反:由于 PCRE 无法完成此操作,您必须添加更多代码,这可能会导致更多问题。 - Charles
我指的是数组中的额外元素。 - Maerlyn
如果存在我所要求的这个修饰符的相反修饰符,那么就不需要那一行额外的代码了。如果有一个全局修饰符可以设置所有pcre函数的首选项,以设置输出数组的模式,那将更好。我再重复一遍:这是在PDO对象中完成的,为什么不能在pcre中完成呢? - rsk82
就像你所说的:PDO是基于对象的,而pcre仍然是基于函数的。随着语言的发展,它们可能会在以后的某个时候进行更改。 - Maerlyn
你还可以使用T-Regx库,该库具有namedGroups()方法:D - Danon

3
与hakre在上面发布的答案相似,我使用以下代码片段仅获取已命名参数:
$subject = "This is some text written on 2010-07-18.";
$pattern = '|(?<date>\d\d\d\d-\d\d-\d\d)|i';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
echo '<pre>Before Diff: ', print_r($matches, 1), '</pre>';
$matches = array_diff_key($matches[0], range(0, count($matches[0])));
echo '<pre>After Diff: ', print_r($matches, 1), '</pre>';

...生成以下内容:

Before Array
(
    [0] => Array
        (
            [0] => 2010-07-18
            [date] => 2010-07-18
            [1] => 2010-07-18
        )

)
After Array
(
    [date] => 2010-07-18
)

2

在返回之前还可以取消所有数字索引:

foreach (range(0, floor(count($arr_result) / 2)) as $index) {
    unset($arr_result[$index]);
}

1
我在您的帖子中看到,这些可能是将来内存等的超载...... 在这种情况下,为什么不能用 unset() 来解决呢?
$string = "This is some text written on 2010-07-18.";
preg_match('|(?<date>\d{4}-\d{2}-\d{2})|i', $string, $arr_result);
$date = array("date" => $arr_result['date']);
unset($arr_result, $string);//delete array and string preg_match origen

print_r($date);
//or create a new:
//  $arr_result = $date;
//print_r($arr_result);

1
你可以使用 T-Regx 并选择 group()namedGroups(),这些方法仅返回命名捕获组。
<?php
$subject = "This is some text written on 2010-07-18.";

pattern('(?<date>\d\d\d\d-\d\d-\d\d)', 'i')->match($subject)->first(function ($match) {

    $date = $match->get('date'); 
    // 2010-07-18

    $groups = $match->namedGroups(); 
    // [
    //   'date' => '2010-07-18'
    // ]   
});

0

我使用了一些介绍的代码,这是在php 5.6+上运行的最终代码:

$re = '/\d+\r\n(?<start>[\d\0:]+),\d+\s--\>\s(?<end>[\d\0:]+),.*\r\nHOME.*\r\nGPS\((?<x>[\d\.]+),(?<y>[\d\.]+),(?<d>[\d\.]+)\)\sBAROMETER\:(?<h>[\d\.]+)/';

$str= file_get_contents($srtFile);
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
echo '<pre>';
$filtered=array_map(function ($d){
     return $array_filtered = array_filter($d, "is_string", ARRAY_FILTER_USE_KEY);
    },$matches);
var_dump($filtered);

如果你感兴趣,它的作用是从DJI无人机录制视频时生成的str文件中读取位置数据。


-2

试试这个:

$string = "This is some text written on 2010-07-18."; 
preg_match('|(?<date>\d\d\d\d-\d\d-\d\d)|i',$string,$arr_result);
echo $arr_result['date'];

不,那是错误的,我指的是一个修饰符,可以自动删除数字键,对于 preg_match_all 可能产生的多分支数组来说并不容易。 - rsk82

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接