当模式中的捕获组被重复使用时,以前的值将被最后一个值覆盖。因此,不能像这样使用 preg_match
设计您的模式。
一种可能的解决方案是使用 preg_match_all
查找模式的所有出现以及 \G
锚点,它是上一个匹配之后的位置。模式必须编写为一次只查找一个值。
\G
确保所有匹配都是连续的。为确保已到达字符串的末尾(换句话说,从开头到结尾正确格式化了字符串),一种便捷的方法是在末尾创建一个空的捕获组。因此,如果该捕获组出现在最后一次匹配中,则表示格式正确。
define('PARSE_SENTENCE_PATTERN', '~
(?: # two possible beginings:
\G(?!\A) # - immediatly after a previous match
| # OR
\A # - at the start of the string
(?<subject> \w+ (?>[-.]\w+)*? ) -is- # (in this case the subject is captured)
)
(?<value> \w+ (?>[-.]\w+)*? ) # capture the value
(?: -or- | \z (?<check>) ) # must be followed by "-or-" OR the end of the string \z
# (then the empty capture group "check" is created)
~x');
function parseSentence ($sentence) {
if (preg_match_all(PARSE_SENTENCE_PATTERN, $sentence, $matches, PREG_SET_ORDER) &&
isset(end($matches)['check']) )
return [ 'subject' => $matches[0]['subject'],
'values' => array_reduce ($matches, function($c, $v) {
$c[] = $v['value']; return $c; }, $c = []) ];
return false; // wrong format
}
// tests
$test_strings = ['accuracy-is-5', 'accuracy-is-5-or-15', 'accuracy-is-5-or-15-or-20',
'package-is-dip-8-or-dip-4-or-dip-16',
'bad-format', 'bad-format-is-', 'bad-format-is-5-or-'];
foreach ($test_strings as $test_string) {
var_dump(parseSentence($test_string));
}