我有一个字符串,其正确的语法是正则表达式^([0-9]+[abc])+$
。所以有效字符串的例子是:' 1a2b'或' 00333b1119a555a0c'
为清楚起见,字符串是(值,字母)对的列表,顺序很重要。我坚持使用输入字符串,所以我无法改变它。虽然在上面使用上述正则表达式来测试正确的语法似乎很容易,但我试图想到PHP中最有效的方法是将兼容字符串转换为可用的数组,如下所示:
输入:
'00333b1119a555a0c'
输出:
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')
)
我在使用preg_match方面遇到了困难。例如,这并没有给出预期的结果,意图是在EITHER \ d +上贪婪匹配(并保存)OR [abc](并保存),重复直到到达字符串结尾。
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:(\d+|[abc]))+$/", $text, $out);
这也没有用,这里的意图是在\ d + [abc]上贪婪匹配(并保存这些),重复直到字符串结束,然后将它们分成数字和字母。
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:\d+[abc])+$/", $text, $out);
我计划将语法检查为preg_match的一部分,然后使用preg_match输出来贪婪地匹配'块' (或者如果使用preg_split则保留分隔符),然后如果需要使用for (...; i+=2)
一次循环结果2项以提取其对中的值字母。
但我似乎无法让基本的preg_split()或preg_match()方法顺利运作,更不用说探索是否有一个整洁的' neater'或更有效的方式。
答案 0 :(得分:4)
你的正则表达式需要几个匹配的组
/([0-9]+?)([a-z])/i
这意味着匹配一个组中的所有数字,以及另一个组中的所有字母。 Preg match all获得所有匹配。
正则表达式的关键是非贪婪标志?
,它匹配最短的字符串。
match[0]
是整场比赛
match[1]
是第一个匹配组(数字)
match[2]
是第二个匹配组(字母)
以下示例
<?php
$input = '00333b1119a555a0c';
$regex = '/([0-9]+?)([a-z])/i';
$out = [];
$parsed = [];
if (preg_match_all($regex, $input, $out)) {
foreach ($out[0] as $index => $value) {
$parsed[] = [
'num' => $out[1][$index],
'let' => $out[2][$index],
];
}
}
var_dump($parsed);
输出
array(4) {
[0] =>
array(2) {
'num' =>
string(5) "00333"
'let' =>
string(1) "b"
}
[1] =>
array(2) {
'num' =>
string(4) "1119"
'let' =>
string(1) "a"
}
[2] =>
array(2) {
'num' =>
string(3) "555"
'let' =>
string(1) "a"
}
[3] =>
array(2) {
'num' =>
string(1) "0"
'let' =>
string(1) "c"
}
}
答案 1 :(得分:3)
使用preg_match_all
(带PREG_SET_ORDER
标记)和array_map
函数的简单解决方案:
$input = '00333b1119a555a0c';
preg_match_all('/([0-9]+?)([a-z]+?)/i', $input, $matches, PREG_SET_ORDER);
$result = array_map(function($v) {
return ['num' => $v[1], 'let' => $v[2]];
}, $matches);
print_r($result);
输出:
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
)
)
答案 2 :(得分:2)
您可以使用:
public void updateNewList(ArrayList<data> array) {
yourAdapterArrayList = array;
//notify data set change here
}
<强>输出:强>
$str = '00333b1119a555a0c';
$arr=array();
if (preg_match_all('/(\d+)(\p{L}+)/', $str, $m)) {
array_walk( $m[1], function ($v, $k) use(&$arr, $m ) {
$arr[] = [ 'num'=>$v, 'let'=>$m[2][$k] ]; });
}
print_r($arr);
答案 3 :(得分:0)
以上所有工作。但他们似乎没有我想要的优雅 - 他们需要循环,使用数组映射,或者(对于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);
输入+重复检查的更完整版本
$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);
<强>解释强>
这使用@RomanPerekhrest和@exussum建议的preg_match_all()来分解各个组并拆分数字和字母。我使用了命名组,以便使用正确的名称创建$ raw_matches的结果数组。
但是如果不期望重复,那么我使用了array_column()的额外步骤,它直接从嵌套的条目数组中提取数据并创建所需的平面数组,而不需要循环,映射,遍历或分配项目按项目:从
(group1 => (num1, let1), group2 => (num2, let2), ... )
到&#34; flat&#34;阵列:
(let1 => num1, let2 => num2, ... )
如果命名的正则表达式匹配感觉太高级,那么它们可以被忽略 - 无论如何匹配将被赋予数字,这也将起作用,你必须手动分配字母,它更难以遵循。< / p>
preg_match_all("/(\d+)([dhm])/", $input, $raw_matches, PREG_SET_ORDER);
$matches = array_column($raw_matches, 1, 2);
如果您需要检查重复的字母(这不是问题但可能有用),请按照以下方法:如果原始匹配包含任何字母的&gt; 1条目,那么当array_column( )使用此字母成为新数组的键,重复键不存在。每个字母只保留一个条目。所以我们只测试最初找到的匹配数是否与array_coulmn之后的最终数组中的匹配数相同。如果没有,那就有重复。