首先,
为什么您尝试的模式没有产生期望的输出?因为
$
锚点告诉函数使用最后四个数字作为“分隔符”(在将字符串分成单独部分时应该消耗的字符)来拆分字符串。
您的结果:
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
)
简单来说,要修复您的模式,您必须:
- 在分解时不消耗任何字符,并且
- 确保不生成任何空元素。
我的片段在此帖子底部。
其次,关于使用哪个正则表达式函数(甚至是否应该使用正则表达式作为首选工具)似乎存在一些争议。
- 我的立场是,使用非正则表达式的方法将需要一长串代码行,这些代码行将与正则表达式模式一样难以阅读,甚至更加困难。使用正则表达式可以让你在一行中生成结果,而不会以不美观的方式呈现。因此,让我们放弃为此任务编写迭代条件集。
现在关键问题是,这个任务是否仅仅是从一个一致且有效的字符串中“提取”数据(情况“A”),还是“验证并提取”来自字符串的数据(情况“B”),因为输入不能百分之百地保证一致/正确。
- 在情况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 步 (演示)
~\d{4}\K~
25 步 (演示)
/[a-z]+[0-9]{4}\K/i
13 步 (演示)
~\D+[0-9]{4}\K~
13 步 (演示)
~\D+\d{4}\K~
13 步 (演示)
请注意,\K
是一个元字符,它的含义是“重新启动完整字符串匹配”,换句话说,“忘记/释放到此为止之前所有已匹配的字符”。这有效地确保在分割过程中没有任何字符会丢失。
建议的技术:(演示)
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',
)