匹配令牌及其上下文(可能重叠)

时间:2019-09-23 17:31:16

标签: php regex pcre

我正在处理一些文本文件,想要找到某些标记,还需要一些围绕它们的文本来获取上下文。 我的问题是,如果标记的每个实例都足够接近,无法被其前面的标记上下文捕获,我就找不到它。

作为示例和简化,假设我想在某些文本中找到每个5位数字,并在其前后分别找到20个字符以获取上下文。

首先,我尝试过类似的操作:

<?php
$text = "Lorem ipsum 11111 dolor sit 22222 amet, consectetur 33333 adipiscing elit, sed do eiusmod tempor 1111 incididunt ut 11111 labore et dolore magna aliqua.";
$nmbrs_tmp = array();
preg_match_all("@.{0,19}[^\d](\d{5})[^\d].{0,19}@s", $text, $nmbrs_tmp);
print_r($nmbrs_tmp);

但是它不会捕获22222,因为它已经在11111的第一次捕获之内并且是上下文:

//output
Array
(
    [0] => Array
        (
            [0] => Lorem ipsum 11111 dolor sit 22222 ame
            [1] => t, consectetur 33333 adipiscing elit, se
            [2] =>  1111 incididunt ut 11111 labore et dolore ma
        )

    [1] => Array
        (
            [0] => 11111
            [1] => 33333
            [2] => 11111
        )

)

然后我尝试了先行和后退,但第1次:lookbehinds必须为固定长度,第2次:我不再捕获上下文了:"@(?<=.{0,19})[^\d](\d{5})[^\d](?=.{0,19})@s" //this won't work

理想情况下,我会喜欢这样的东西,在这里我捕获每个5位数字的实例,并获得所有可能的上下文:

//output
Array
(
    [0] => Array
        (
            [0] => Lorem ipsum 11111 dolor sit 22222 ame
            [1] => sum 11111 dolor sit 22222 amet, consectetur 3
            [2] => 2 amet, consectetur 33333 adipiscing elit, se
            [3] =>  1111 incididunt ut 11111 labore et dolore ma
        )

    [1] => Array
        (
            [0] => 11111
            [1] => 22222
            [2] => 33333
            [3] => 11111
        )

)

如果没有办法使用正则表达式来做到这一点,那么我愿意接受涉及多次阅读文本或使用更多正则表达式的PHP解决方案。

1 个答案:

答案 0 :(得分:0)

这是一种使用匹配偏移量来计算相关子字符串的方法:

<?php
$text = "99999 Lorem ipsum 11111 dolor sit 22222 amet, consectetur 33333 adipiscing elit, sed do eiusmod tempor 1111 incididunt ut 11111 labore et dolore magna aliqua. 99999";
$nmbrs_tmp = array();
preg_match_all("@\b\d{5}\b@s", $text, $nmbrs_tmp, PREG_OFFSET_CAPTURE);

foreach ($nmbrs_tmp[0] as $key => $field) {
    $offset = $field[1];
    $start = ( $offset>=20 ? $offset-20 : 0 );
    $length = $offset>=20 ? 45 : 45-(20-$offset);
    $nmbrs_tmp[0][$key][2] = substr( $text, $start, $length );
}

print_r($nmbrs_tmp);

首先,我们将正则表达式简化为仅查找5位数字(您原来的正则表达式会在行首和末尾丢失数字)。

然后我们进行匹配,并传递PREG_OFFSET_CAPTURE标志。

最后,我们使用返回的偏移量来计算所需子字符串的长度($length是否落在输入的末尾可能并不重要,但您可以根据需要进行调整)。

结果是:

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => 99999
                    [1] => 0
                    [2] => 99999 Lorem ipsum 11111 d
                )

            [1] => Array
                (
                    [0] => 11111
                    [1] => 18
                    [2] => 99999 Lorem ipsum 11111 dolor sit 22222 ame
                )

            [2] => Array
                (
                    [0] => 22222
                    [1] => 34
                    [2] => sum 11111 dolor sit 22222 amet, consectetur 3
                )

            [3] => Array
                (
                    [0] => 33333
                    [1] => 58
                    [2] => 2 amet, consectetur 33333 adipiscing elit, se
                )

            [4] => Array
                (
                    [0] => 11111
                    [1] => 122
                    [2] =>  1111 incididunt ut 11111 labore et dolore ma
                )

            [5] => Array
                (
                    [0] => 99999
                    [1] => 159
                    [2] => olore magna aliqua. 99999
                )

        )

)