【问题标题】:PHP how to avoid mixed letter-number when extracting chunks of numbers from stringPHP如何在从字符串中提取数字块时避免混合字母数字
【发布时间】:2017-10-25 15:58:49
【问题描述】:

我正在编写一个 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 这样更复杂的模式会通过过滤器。这就是我有点卡住的地方。

回顾:

  • 从字符串中提取数量不定的数字块。
  • 该字符串至少包含 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 )'

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

【问题讨论】:

  • 所以你想只提取完全是数字的下划线分隔的子字符串并拒绝其他所有内容?
  • 这样的? eval.in/886873 - 我没有发布答案,因为如果我理解你问题的措辞,我不是 100%。
  • $test2 = "123_123_234_1Foo2"; $ints = array_filter(explode('_', $test2 ), 'is_numeric'); var_dump($ints);

标签: php regex


【解决方案1】:

如果我正确理解您的问题,您希望拆分下划线分隔的字符串,并过滤掉所有非数字的子字符串。如果是这样,这可以在没有正则表达式的情况下实现,使用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 仍然是一个字符串数组,尽管是数字。

希望这会有所帮助:)

【讨论】:

  • @DarraghEnright $digits = array_filter(explode('_', $str ), 'ctype_digit'); 您可以将该函数作为 array_filter 的第二个参数
  • @EmilioGort - 绝对!这是真的,因为它是单行的,因此非常简洁/可读。我知道,但我更喜欢编写显式回调 - 我想这只是我的风格 :) 应该注意的是,回调比将可调用对象作为字符串传递要快得多。但是,我首先要承认“争论”(如果你能原谅双关语的话)是一种微优化! ;)
【解决方案2】:

在explode之后只获取字符串的数字部分

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

【讨论】:

  • is_numeric() 将返回 true 如果它确定一个字符串是浮点数。在 PHP 7.* 之前,它还返回 true 用于表示十六进制和二进制数字的字符串。这对 OP 来说可能不是问题,但值得标记。
  • 注意:最好的选项 IFF 下划线是字符串应该被分割的 only 分隔符。如果字符串也需要在其他分隔符上拆分( _ - / \ )等,strtok 是更好的选择。
【解决方案3】:

使用strtok

正则表达式不是灵丹妙药,有更简单的解决方法可以解决您的问题,尤其是考虑到您要在分隔符上进行拆分。

以下任何一种方法都会更简洁、更易于维护,而strtok() 方法可能性能更好:

  1. 使用explode 创建并循环遍历数组,检查每个值。
  2. 使用preg_split 来做同样的事情,但采用更具适应性的方法。
  3. 使用 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

【讨论】:

    猜你喜欢
    • 2021-03-15
    • 1970-01-01
    • 2021-12-19
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多