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

Match token and it's context with possible overlapping

我正在处理一些文本文件并希望找到某些标记以及围绕它们的一些文本以获得一些上下文。 我的问题是,如果标记足够接近以被它前面的标记的上下文捕获,我无法找到标记的每个实例。

作为示例和简化,假设我想在某些文本中找到每个 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
        )

)

然后我尝试使用前瞻和后视,但第一:后视必须是固定长度,第二:我不会再捕获上下文:"@(?<=.{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 涉及多次浏览文本或使用更多正则表达式的解决方案。

这里有一个使用匹配偏移量来计算相关子串的方法:

<?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
                )

        )

)