Sti*_*lez 8 php regex tokenize regex-greedy
我有一个字符串,正确的语法是正则表达式^([0-9]+[abc])+$
.所以有效字符串的例子是:'1a2b'或'00333b1119a555a0c'
为清楚起见,字符串是(值,字母)对的列表,并且顺序很重要.我坚持使用输入字符串,所以我不能改变它.虽然使用上面的正则表达式来测试正确的语法似乎很容易,但我正在尝试考虑PHP中最有效的方法将兼容字符串转换为可用的数组,如下所示:
输入:
'00333b1119a555a0c'
Run Code Online (Sandbox Code Playgroud)
输出:
array (
0 => array('num' => '00333', 'let' => 'b'),
1 => array('num' => '1119', 'let' => 'a'),
2 => array('num' => '555', 'let' => 'a'),
3 => array('num' => '0', 'let' => 'c')
)
Run Code Online (Sandbox Code Playgroud)
我在使用preg_match方面遇到了困难.例如,这没有给出预期的结果,意图是在EITHER\d +上贪婪匹配(并保存)OR [abc](并保存),重复直到到达字符串结尾.
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:(\d+|[abc]))+$/", $text, $out);
Run Code Online (Sandbox Code Playgroud)
这也不起作用,这里的意图是在\ d + [abc]上贪婪匹配(并保存这些),重复直到字符串到达结束,然后将它们分成数字和字母.
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:\d+[abc])+$/", $text, $out);
Run Code Online (Sandbox Code Playgroud)
如果使用需要遍历结果2项在时间我计划检查语法作为的preg_match的一部分,然后使用的preg_match输出到贪婪-匹配"块"(或使用使preg_split保持分隔符如果),然后for (...; i+=2)
到在他们的对中提取值字母.
但我似乎无法让基本的preg_split()或preg_match()方法顺利运行,更不用说探索是否有"整洁"或更有效的方法.
以上所有工作。但它们似乎没有我想要的优雅——它们需要循环、使用数组映射,或者(对于 preg_match_all())它们还需要另一个几乎相同的正则表达式,只是为了验证字符串与正则表达式匹配。
我最终发现 preg_match_all()与命名捕获相结合为我解决了这个问题。我以前没有为此目的使用过命名捕获,它看起来很强大。
如果不需要重复,我还添加了一个可选的额外步骤来简化输出(这不在问题中,但可能对某人有帮助)。
$input = '00333b1119a555a0c';
preg_match_all("/(?P<num>\d+)(?P<let>[dhm])/", $input, $raw_matches, PREG_SET_ORDER);
print_r($raw_matches);
// if dups not expected this is also worth doing
$matches = array_column($raw_matches, 'num', 'let');
print_r($matches);
Run Code Online (Sandbox Code Playgroud)
更完整的版本,带有输入+重复检查
$input = '00333b1119a555a0c';
if (!preg_match("/^(\d+[abc])+$/",$input)) {
// OPTIONAL: detected $input incorrectly formatted
}
preg_match_all("/(?P<num>\d+)(?P<let>[dhm])/", $input, $raw_matches, PREG_SET_ORDER);
$matches = array_column($raw_matches, 'num', 'let');
if (count($matches) != count($raw_matches)) {
// OPTIONAL: detected duplicate letters in $input
}
print_r($matches);
Run Code Online (Sandbox Code Playgroud)
解释:
这使用 @RomanPerekhrest 和 @exussum 建议的 preg_match_all() 来分解各个组并分割数字和字母。我使用命名组,以便已使用正确的名称创建 $raw_matches 的结果数组。
但如果不需要重复,那么我使用了 array_column() 的额外步骤,它直接从嵌套的条目数组中提取数据并创建所需的平面数组,而不需要循环、映射、遍历或逐项分配:从
(group1 => (num1, let1), group2 => (num2, let2), ... )
Run Code Online (Sandbox Code Playgroud)
到“平面”数组:
(let1 => num1, let2 => num2, ... )
Run Code Online (Sandbox Code Playgroud)
如果命名的正则表达式匹配感觉太高级,那么它们可以被忽略 - 无论如何,匹配都会被赋予数字,这也同样有效,您必须手动分配字母,而且更难遵循。
preg_match_all("/(\d+)([dhm])/", $input, $raw_matches, PREG_SET_ORDER);
$matches = array_column($raw_matches, 1, 2);
Run Code Online (Sandbox Code Playgroud)
如果您需要检查重复的字母(这不在问题中,但可能有用),请按以下方法操作:如果原始匹配项包含任何字母的 >1 个条目,那么当使用 array_column() 时,该字母将成为新数组,并且不能存在重复的键。每个字母仅保留一个条目。所以我们只是测试最初找到的匹配项数量是否与 array_column 之后的最终数组中的匹配项数量相同。如果没有,则存在重复项。