使用正则表达式匹配所有以4位数结尾的子串

时间:2016-11-03 13:42:27

标签: php regex string preg-split

我试图在php中拆分一个字符串,如下所示:

ABCDE1234ABCD1234ABCDEF1234

进入一个字符串数组,在这种情况下,它将如下所示:

ABCDE1234
ABCD1234
ABCDEF1234

因此模式是“未定义的字母数,然后是4位数,然后是未定义的字母数和4位数等。”

我正在尝试使用preg_split分割字符串,如下所示:

$pattern = "#[0-9]{4}$#";
preg_split($pattern, $stringToSplit);

它返回一个包含第一个元素中完整字符串(不是拆分)的数组。

我猜这里的问题是我的正则表达式,因为我不完全了解如何使用它们,我不确定我是否正确使用它。

那么使用正确的正则表达式是什么?

5 个答案:

答案 0 :(得分:16)

您不希望preg_split,想要preg_match_all

$str = 'ABCDE1234ABCD1234ABCDEF1234';
preg_match_all('/[a-z]+[0-9]{4}/i', $str, $matches);
var_dump($matches);

输出:

array(1) {
  [0]=>
  array(3) {
    [0]=>
    string(9) "ABCDE1234"
    [1]=>
    string(8) "ABCD1234"
    [2]=>
    string(10) "ABCDEF1234"
  }
}

答案 1 :(得分:7)

PHP使用PCRE样式的正则表达式,可以让你做后卫。您可以使用它来查看您后面是否有4位数字。将它与前瞻相结合,看看你前面是否有一封信,你就明白了:

(?<=\d{4})(?=[a-z])

注意 Debuggex Demo页面上的虚线。这些是你想分开的点。

在PHP中,这将是:

var_dump(preg_split('/(?<=\d{4})(?=[a-z])/i', 'ABCDE1234ABCD1234ABCDEF1234'));

答案 2 :(得分:5)

使用对比原则

\D+\d{4}
# requires at least one non digit
# followed by exactly four digits

a demo on regex101.com

<小时/> 在PHP中,这将是:

<?php
$string = 'ABCDE1234ABCD1234ABCDEF1234';
$regex = '~\D+\d{4}~';
preg_match_all($regex, $string, $matches);
?>

请参阅a demo on ideone.com

答案 3 :(得分:1)

我不擅长正则表达式,所以这里的道路较少:

<?php
$s = 'ABCDE1234ABCD1234ABCDEF1234';
$nums = range(0,9);

$num_hit = 0;
$i = 0;
$arr = array();

foreach(str_split($s) as $v)
{
    if(isset($nums[$v]))
    {
        ++$num_hit;
    }

    if(!isset($arr[$i]))
    {
        $arr[$i] = '';
    }

    $arr[$i].= $v;

    if($num_hit === 4)
    {
        ++$i;
        $num_hit = 0;
    }
}

print_r($arr);

答案 4 :(得分:0)

首先,为什么您尝试的模式无法提供所需的输出?因为$锚指示函数通过使用最后四个数字作为“定界符”来使字符串爆炸(将字符串分成单独的部分时应该消耗的字符。

您的结果:

array (
  0 => 'ABCDE1234ABCD1234ABCDEF', // an element of characters before the last four digits
  1 => '',  // an empty element containing the non-existent characters after the four digits
)

以简单的英语,要修复您的图案,您必须:

  1. 爆炸时不消耗任何字符,
  2. 确保不会生成任何空元素。

我的摘要在这篇文章的底部。


第二,关于要使用哪种正则表达式功能(或者即使正则表达式是更可取的工具),似乎存在一些争议。

  • 我的立场是,使用非正则表达式方法将需要长行的行,这与正则表达式模式相比,即使读起来也不困难,也同样。使用正则表达式使您可以单行而不是难看的方式生成结果。因此,让我们处理此任务的迭代条件集​​。
  • 现在最关键的问题是此任务是只是从一致且有效的字符串中“提取”数据(案例“ A”),还是从字符串中“验证并提取”数据(案例“ B”) ”),因为该输入不能被100信任为一致/正确。

    • 对于情况A,您不必担心在输出中生成有效元素,因此preg_split()preg_match_all()是很好的候选人。
    • 在情况B中,preg_split()是不可取的,因为它只寻找定界子字符串-仍然不​​知道字符串中的所有其他字符。
  • 假定此任务为案例A,则仍在等待有关更好的调用函数的决定。好的,这两个函数都会生成一个数组,但是 preg_match_all()会在您需要平面数组的同时创建多维数组(例如preg_split()提供了)。这意味着您需要向全局范围($matches)添加一个新变量,并将[0]附加到数组中以访问所需的全字符串匹配。对于不了解正则表达式模式的人来说,这可能与使用“ magic numbers”的错误做法有关。

对我来说,我努力为直接性和准确性,然后效率,然后简洁明了编写代码。由于在执行如此小的操作时您不太可能注意到性能下降,因此效率并不是非常重要的。我只想进行一些比较,以突出显示仅利用环顾四周的模式或缺少机会贪婪地匹配可预测字符的模式的成本。

  • /(?<=\d{4})(?=[a-z])/i 79个步骤(Demo
  • ~\d{4}\K~ 25个步骤(Demo
  • /[a-z]+[0-9]{4}\K/i 13个步骤(Demo
  • ~\D+[0-9]{4}\K~ 13个步骤(Demo
  • ~\D+\d{4}\K~ 13个步骤(Demo

FYI,\K是一个元字符,表示“重新开始全字符串匹配”,换句话说就是“忘记/释放到目前为止所有以前匹配的字符”。这样可以有效地确保随地吐痰中不会丢失任何字符。

建议的技术:(Demo

var_export(
    preg_split(
        '~\D+\d{4}\K~',                // pattern
        'ABCDE1234ABCD1234ABCDEF1234', // input
        0,                             // make unlimited explosions
        PREG_SPLIT_NO_EMPTY            // exclude empty elements
    )
);

输出:

array (
  0 => 'ABCDE1234',
  1 => 'ABCD1234',
  2 => 'ABCDEF1234',
)