在PHP中对多个字符分割字符串


Split string on multiple characters in PHP

我需要将年龄拆分为其组件,其中年龄表示为eg。27y5m6w2d或这些值的任意组合。如。2w3d或27d或5y2d等。结果必须是多达4个变量$年,$月,$周和$天包含适当的数值。

我可以用这个代码做,但我希望有更有效的东西:

$pos = strpos($age, 'y');
if ($pos !== false)
   list($yrs, $age) = explode('y', $age);
$pos = strpos($age, 'm');
if ($pos !== false)
   list($mths, $age) = explode('m', $age);
$pos = strpos($age, 'w');
if ($pos !== false)
   list($wks, $age) = explode('w', $age);
$pos = strpos($age, 'd');
if ($pos !== false)
   list($days, $age) = explode('d', $age);

如果你有一个建议,请在10,000次迭代循环中运行它并告知结果。上面的代码在10000次迭代中平均运行0.06秒。我使用以下代码进行测试:

<?php
$startTime = microtime(true);
// code goes here
echo "Time:  " . number_format(( microtime(true) - $startTime), 4) . " Seconds<br>"; 
echo 'y='.$yrs.' m='.$mths.' w='.$wks.' d='.$days;
?>

我建议像这样使用与preg_match_all()匹配的正则表达式:

$input = '2w3d'
$matches = array();
preg_match_all('|('d+)([ymwd])|', $input, $matches, PREG_SET_ORDER);

其中输出数组$matches将保存此模式中的所有匹配项:

$matches = array(
    // 0 => matched string, 1 => first capture group, 2 => second capture group 
    0 => array( 0 => '2w', 1 => '2', 2 => 'w' ),
    1 => array( 0 => '3d', 1 => '3', 2 => 'd' )
);
编辑:


像这样处理这个结果:

$yrs = $mths = $wks = $days = 0;
foreach($matches as $match) {
    switch($match[2]) {
        case 'y': $yrs = (int)$match[1]; break;
        case 'm': $mths = (int)$match[1]; break;
        case 'w': $wkss = (int)$match[1]; break;
        case 'd': $days = (int)$match[1]; break;
    }
}


编辑2:Hacky alternative
使用字符比较,10万次迭代大约需要0.4秒。

$number = '';
for($j = 0, $length = strlen($input); $j < $length; $j++) {
    if($input[$j] < 'A') {
        $number .= $input[$j];
    } else {
        switch($input[$j]) {
            case 'y': $yrs = (int)$number; break;
            case 'm': $mths = (int)$number; break;
            case 'w': $wks = (int)$number; break;
            case 'd': $days = (int)$number; break;
        }
        $number = '';
    }
}

我将采用以下方法。

$age = '27y5m6w2d';
// Split the string into array of numbers and words
$arr = preg_split('/(?<=[ymdw])/', $age, -1, PREG_SPLIT_NO_EMPTY);
foreach ($arr as $str) 
{
    $item = substr($str, -1); // Get last character
    $value = intval($str);    // Get the integer
    switch ($item) 
    {
        case 'y':
            $year = $value;
            break;        
        case 'm':
            $month = $value;
            break;
        case 'd':
            $day = $value;
            break;
        case 'w':
            $week = $value;
            break;
    }
}

代码更具可读性,并且稍微快一些。我进行了10000次迭代测试,大约只花了0.0906秒。

您不需要使用查找数组或开关块来膨胀您的代码。

您的输入字符串可预测地格式化(按顺序),因此您可以在每个预期的"单元"处编写包含可选捕获组的单个regex模式。在输入字符串中。虽然使用命名捕获组提供了一些声明性的好处,但它也会使regex模式和输出数组膨胀——所以我通常不喜欢使用它们。

你会注意到在正则表达式中有一个重复的格式:(?:('d+)unitLetter)?。这使得修改/扩展模式变得非常简单。所有这些子模式都使目标子字符串"可选"。子模式的最后一个字母表示被隔离的时间单位。

在本例中,匹配输出结构为:
  • [0]:完整的字符串匹配(我们不需要)
  • [1]: yrs
  • [2]: mths
  • [3]: wks
  • [4]: days

代码(演示):

$strings = ['27y5m6w2d', '1m1w', '2w3d', '999y3w', '27d', '5y2d'];
foreach ($strings as $string) {
    preg_match('~(?:('d+)y)?(?:('d+)m)?(?:('d+)w)?(?:('d+)d)?~', $string, $m);
    var_export([
        'yrs' => $m[1] ?? '',
        'mths' => $m[2] ?? '',
        'wks' => $m[3] ?? '',
        'days' => $m[4] ?? '',
    ]);
    echo "'n---'n";
}
输出:

array (
  'yrs' => '27',
  'mths' => '5',
  'wks' => '6',
  'days' => '2',
)
---
array (
  'yrs' => '',
  'mths' => '1',
  'wks' => '1',
  'days' => '',
)
---
array (
  'yrs' => '',
  'mths' => '',
  'wks' => '2',
  'days' => '3',
)
---
array (
  'yrs' => '999',
  'mths' => '',
  'wks' => '3',
  'days' => '',
)
---
array (
  'yrs' => '',
  'mths' => '',
  'wks' => '',
  'days' => '27',
)
---
array (
  'yrs' => '5',
  'mths' => '',
  'wks' => '',
  'days' => '2',
)
---