我正在编写一个 PHP 函数来从字符串中提取数字 ID,例如:
$test = '123_123_Foo'
一开始我采取了两种不同的方法,一种是
preg_match_all()
:
$test2 = '123_1256_Foo';
preg_match_all('/[0-9]{1,}/', $test2, $matches);
print_r($matches[0]); // Result: 'Array ( [0] => 123 [1] => 1256 )'
以及其他与
preg_replace()
和 explode()
:
$test = preg_replace('/[^0-9_]/', '', $test);
$output = array_filter(explode('_', $test));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
只要字符串不包含混合字母和数字,其中任何一个都可以正常工作,例如:
$test2 = '123_123_234_Foo2'
明显的结果是 Array ( [0] => 123 [1] => 1256 [2] => 2 )
所以我编写了另一个正则表达式来摆脱混合字符串:
$test2 = preg_replace('/([a-zA-Z]{1,}[0-9]{1,}[a-zA-Z]{1,})|([0-9]{1,}[a-zA-Z]{1,}[0-9]{1,})|([a-zA-Z]{1,}[0-9]{1,})|([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', '', $test2);
$output = array_filter(explode('_', $test2));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
问题也很明显,像 Foo2foo12foo1 这样更复杂的模式会通过过滤器。这就是我有点卡住的地方。
回顾:
由于只需要前半部分,我决定将第一次出现的字母或混合数字字母与
preg_split()
: 分开
$test2 = '123_123_234_1Foo2'
$output = preg_split('/([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', $test, 2);
preg_match_all('/[0-9]{1,}/', $output[0], $matches);
print_r($matches[0]); // Results: 'Array ( [0] => 123 [1] => 123 [2] => 234 )'
我的问题的重点是是否有更简单、更安全或更有效的方法来实现这个结果。
explode()
、array_filter()
和 ctype_digit()
;例如:
<?php
$str = '123_123_234_1Foo2';
$digits = array_filter(explode('_', $str), function ($substr) {
return ctype_digit($substr);
});
print_r($digits);
这产生:
Array
(
[0] => 123
[1] => 123
[2] => 234
)
请注意
ctype_digit()
:
检查提供的字符串中的所有字符是否都是数字。
所以
$digits
仍然是一个字符串数组,尽管是数字。
希望这有帮助:)
正则表达式不是灵丹妙药,对于您的问题有更简单的解决方案,特别是考虑到您试图在分隔符上进行拆分。
以下任何方法都会更干净,更易于维护,并且
strtok()
方法会可能表现更好:
您的案例的基本示例:
function strGetInts(string $str, str $delim) {
$word = strtok($str, $delim);
while (false !== $word) {
if (is_integer($word) {
yield (int) $word;
}
$word = strtok($delim);
}
}
$test2 = '123_1256_Foo';
foreach(strGetInts($test2, '_-') as $key {
print_r($key);
}
注意: strtok 的第二个参数是包含用于分割字符串的任何分隔符的字符串。因此,我的示例将结果分组为由下划线或破折号分隔的字符串。
附加说明:当且仅当字符串仅需要在单个分隔符(仅下划线)上拆分时,使用
explode
的方法可能会带来更好的性能。对于这样的解决方案,请参阅此线程中的其他答案:https://stackoverflow.com/a/46937452/1589379 .
爆炸后仅获取字符串的数字部分
$test2 = "123_123_234_1Foo2";
$digits = array_filter(
explode('_', $test2 ),
'ctype_digit'
);
var_dump($digits);
结果
array(3) {
[0]=>
string(3) "123"
[1]=>
string(3) "123"
[2]=>
string(3) "234"
}
此任务只需一次
preg_match_all()
调用即可完成。
使用与一个或多个数字匹配的模式:
代码:(演示)
$test2 = '123_123_234_1Foo2';
preg_match_all('/(?<=^|_)\d+(?=_|$)/', $test2, $m);
var_export($m[0]);