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

时间:2017-10-25 15:58:50

标签: php regex

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

我的问题是,是否有更简单,更安全或更有效的方法来实现这一结果。

3 个答案:

答案 0 :(得分:2)

使用strtok

正则表达式不是一个神奇的子弹,并且有针对您的问题的FAR更简单的修复,特别是考虑到您试图拆分分隔符。

以下任何一种方法都更清晰,更易于维护,而strtok()方法可能表现更好:

  1. 使用explode创建并循环遍历数组,检查每个值。
  2. 使用preg_split执行相同操作,但使用更多适应性方法。
  3. 使用strtok,因为它是专为此用例设计的。
  4. 您案件的基本例子:

    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

答案 1 :(得分:2)

如果我正确理解了您的问题,您希望拆分下划线分隔的字符串,并过滤掉任何非数字的子字符串。如果是这样,可以在没有正则表达式的情况下实现,explode()array_filter()ctype_digit(); e.g:

<?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仍然是一个字符串数组,尽管是数字。

希望这会有所帮助:)

答案 2 :(得分:2)

在爆炸后获取字符串的数字部分

$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" }