regex PHP从字符串中提取数字块时如何避免字母数字混合

ryevplcw  于 2023-03-04  发布在  PHP
关注(0)|答案(4)|浏览(209)

我正在编写一个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'

明显的结果是数组([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就可以通过过滤器,这就是我遇到的问题。
总结:

  • 从字符串中提取一个可变数量的数字块。
  • 该字符串至少包含1个数字,并且可以包含由下划线分隔的其他数字和字母。
  • 只能提取前后不带字母的数字。
  • 只有字符串前半部分的数字才重要。

由于只需要前半部分,我决定使用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 )'

我想问的是,有没有更简单、更安全或更有效的方法可以达到这个效果?

42fyovps

42fyovps1#

如果我没理解错的话,你想拆分一个下划线分隔的字符串,并过滤掉任何非数字的子字符串,如果是这样的话,这可以不用regex,用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仍然是一个字符串数组,尽管是数值。
希望这有帮助:)

tpxzln5u

tpxzln5u2#

只获取分解后字符串的数字部分

$test2  = "123_123_234_1Foo2";
$digits = array_filter(explode('_', $test2 ), 'is_numeric');
var_dump($digits);

结果

array(3) { [0]=> string(3) "123" [1]=> string(3) "123" [2]=> string(3) "234" }
vaqhlq81

vaqhlq813#

使用strtok

Regex不是灵丹妙药,对于您的问题,有更简单的解决方法,特别是考虑到您正试图在分隔符上进行拆分。
下面的任何一种方法都会更干净,更易于维护,而strtok()方法 * 可能 * 会表现得更好:
1.使用explode创建并循环遍历一个数组,检查每个值。
1.使用preg_split也可以做同样的事情,但是使用了一种适应性更强的方法。
1.使用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的第二个参数是包含ANY分隔符的字符串,因此,我的示例将结果分组为用下划线或破折号分隔的字符串。
    • 附加注意:**当且仅当字符串只需要在单个分隔符(仅下划线)上拆分时,使用explode的方法可能会带来更好的性能。对于这样的解决方案,请参阅本主题中的其他答案:https://stackoverflow.com/a/46937452/1589379.
5cg8jx4n

5cg8jx4n4#

此任务只需通过一个preg_match_all()调用即可完成。
使用匹配一个或多个数字的模式,该模式为:
1.前面带有字符串的开头或下划线,并且
1.后跟下划线或字符串结尾。
代码:(Demo

$test2 = '123_123_234_1Foo2';
preg_match_all('/(?<=^|_)\d+(?=_|$)/', $test2, $m);
var_export($m[0]);

相关问题