PHP 正则表达式 preg_match_all 在当前匹配之前重复匹配的单词
PHP RegEx preg_match_all Reiterate Matched Words Before the Current Match
我有以下 RegEx 代码
$str = 'word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10';
$matches = array();
preg_match_all('/(\w* ){1,3}keyword( \w*){1,3}/u', $str, $matches);
我希望比赛包括:
word1 word2 word3 keyword word4 word5 word6
word4 word5 word6 keyword word7 word8 word9
但实际上,我得到了这些:
word1 word2 word3 keyword word4 word5 word6
keyword word7 word8 word9
换句话说,由于第一个匹配,第二个匹配被裁剪。
这是一个测试:
https://regex101.com/r/EPp14b/1/
如果您不想交叉单词 keyword
,您可以在重复 1-3 个单词时使用否定前瞻来断言它们不是关键字。
匹配后,您可以使用带捕获组的正向先行断言,匹配 1-3 个再次不是 keyword
的词
句子将是完整匹配项和组 1 的串联。
(?<!\S)(?:(?!keyword\b)\w+\h+){1,3}keyword\b(?=((?:\h+(?!keyword\b)\w+){1,3}))
模式匹配:
(?<!\S)
断言左侧空白边界
(?:
非捕获组
(?!keyword\b)\w+\h+
否定前瞻,如果不是 keyword
,则匹配一个单词和空格
){1,3}
关闭非捕获组重复1-3次
keyword\b
匹配 keyword
(?=
正面前瞻
(
捕获 组 1
(?:\h+(?!keyword\b)\w+){1,3}
匹配1-3个不以keyword
开头的单词
)
关闭组 1
)
关闭前瞻
$re = '/(?<!\S)((?:(?!keyword\b)\w+\h+){1,3}keyword\b)(?=((?:\h+(?!keyword\b)\w+){1,3}))/u';
$strings = [
"word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10",
"word2 keyword word4 word5 word6 keyword word7 word8",
"word2 word3 keyword word4 word5 word6 keyword word7 keyword word10",
];
foreach ($strings as $str) {
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
$matches = array_map(function($m) {
return $m[1] . $m[2];
}, $matches);
print_r($matches);
}
输出
Array
(
[0] => word1 word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8 word9
)
Array
(
[0] => word2 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8
)
Array
(
[0] => word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7
[2] => word7 keyword word10
)
另一种选择是将完整匹配放入前瞻中的捕获组中,以便能够获得重叠匹配:
(?=((\b(?:\w+\h+){1,3}keyword)(?:\h+\w+){1,3}))(?2)
代码:
$s = 'word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10';
$re = '/(?=((\b(?:\w+\h+){1,3}keyword)(?:\h+\w+){1,3}))(?2)/u';
preg_match_all($re, $s, $m);
print_r($m[1]);
/* Output
Array
(
[0] => word1 word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8 word9
)
*/
正则表达式详细信息:
(?=
: 开始前瞻
(
: 开始捕获组#1
(
: 开始捕获组#2
\b
: 字边界
(?:\w+\h+){1,3}
: 匹配1到3个词
keyword
:
)
: 结束捕获组#2
(?:\h+\w+){1,3}
: 匹配1到3个词
)
: 结束捕获组#1
)
:结束先行
(?2)
:递归捕获组#2
您需要的整个部分(关键字 + 周围的词)都在前瞻断言中的捕获组(结果)内,这样字符就不会被消耗,并且可以成为稍后最终下一次匹配的一部分。
但是为了避免多次匹配同一个关键词,需要到达这个之后的位置,消耗所有的字符,直到包含这个关键词。这就是为什么我定义了一个名为 consume 的组并且我引用了他的内容:\g{consume}
.
$pattern = '~
\b
(?=
(?<result>
(?<consume>
(?> \w+ \h+ ){0,3}?
keyword \b
)
(?: \h+ (?! keyword \b ) \w+ ){0,3}
)
) \g{consume}
~ux';
使用此模式,您不必重新构造结果,所有结果都存储在命名组 result:
中
preg_match_all($pattern, $str, $matches);
print_r($matches['result']);
我有以下 RegEx 代码
$str = 'word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10';
$matches = array();
preg_match_all('/(\w* ){1,3}keyword( \w*){1,3}/u', $str, $matches);
我希望比赛包括:
word1 word2 word3 keyword word4 word5 word6
word4 word5 word6 keyword word7 word8 word9
但实际上,我得到了这些:
word1 word2 word3 keyword word4 word5 word6
keyword word7 word8 word9
换句话说,由于第一个匹配,第二个匹配被裁剪。
这是一个测试: https://regex101.com/r/EPp14b/1/
如果您不想交叉单词 keyword
,您可以在重复 1-3 个单词时使用否定前瞻来断言它们不是关键字。
匹配后,您可以使用带捕获组的正向先行断言,匹配 1-3 个再次不是 keyword
句子将是完整匹配项和组 1 的串联。
(?<!\S)(?:(?!keyword\b)\w+\h+){1,3}keyword\b(?=((?:\h+(?!keyword\b)\w+){1,3}))
模式匹配:
(?<!\S)
断言左侧空白边界(?:
非捕获组(?!keyword\b)\w+\h+
否定前瞻,如果不是keyword
,则匹配一个单词和空格
){1,3}
关闭非捕获组重复1-3次keyword\b
匹配keyword
(?=
正面前瞻(
捕获 组 1(?:\h+(?!keyword\b)\w+){1,3}
匹配1-3个不以keyword
开头的单词
)
关闭组 1
)
关闭前瞻
$re = '/(?<!\S)((?:(?!keyword\b)\w+\h+){1,3}keyword\b)(?=((?:\h+(?!keyword\b)\w+){1,3}))/u';
$strings = [
"word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10",
"word2 keyword word4 word5 word6 keyword word7 word8",
"word2 word3 keyword word4 word5 word6 keyword word7 keyword word10",
];
foreach ($strings as $str) {
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
$matches = array_map(function($m) {
return $m[1] . $m[2];
}, $matches);
print_r($matches);
}
输出
Array
(
[0] => word1 word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8 word9
)
Array
(
[0] => word2 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8
)
Array
(
[0] => word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7
[2] => word7 keyword word10
)
另一种选择是将完整匹配放入前瞻中的捕获组中,以便能够获得重叠匹配:
(?=((\b(?:\w+\h+){1,3}keyword)(?:\h+\w+){1,3}))(?2)
代码:
$s = 'word1 word2 word3 keyword word4 word5 word6 keyword word7 word8 word9 word10';
$re = '/(?=((\b(?:\w+\h+){1,3}keyword)(?:\h+\w+){1,3}))(?2)/u';
preg_match_all($re, $s, $m);
print_r($m[1]);
/* Output
Array
(
[0] => word1 word2 word3 keyword word4 word5 word6
[1] => word4 word5 word6 keyword word7 word8 word9
)
*/
正则表达式详细信息:
(?=
: 开始前瞻(
: 开始捕获组#1(
: 开始捕获组#2\b
: 字边界(?:\w+\h+){1,3}
: 匹配1到3个词keyword
:
)
: 结束捕获组#2(?:\h+\w+){1,3}
: 匹配1到3个词
)
: 结束捕获组#1
)
:结束先行(?2)
:递归捕获组#2
您需要的整个部分(关键字 + 周围的词)都在前瞻断言中的捕获组(结果)内,这样字符就不会被消耗,并且可以成为稍后最终下一次匹配的一部分。
但是为了避免多次匹配同一个关键词,需要到达这个之后的位置,消耗所有的字符,直到包含这个关键词。这就是为什么我定义了一个名为 consume 的组并且我引用了他的内容:\g{consume}
.
$pattern = '~
\b
(?=
(?<result>
(?<consume>
(?> \w+ \h+ ){0,3}?
keyword \b
)
(?: \h+ (?! keyword \b ) \w+ ){0,3}
)
) \g{consume}
~ux';
使用此模式,您不必重新构造结果,所有结果都存储在命名组 result:
中preg_match_all($pattern, $str, $matches);
print_r($matches['result']);