如何在字符串中查找和合并序列号?

时间:2016-06-16 12:31:57

标签: php regex

我正在使用PHP处理文本,需要在括号中查找数字并合并序列号,例如

$raw = "outside 1,2,3,11,12,13 (5,6,11,12,13,14,16,19,20,21)";

$result = "outside 1,2,3,11,12,13 (5,6,11-14,16,19-21)";

我不期待完整的代码,但不知道可以做什么策略。我想这个程序是

  1. 在括号中找到数字(通过正则表达式)。
  2. 然后,检查数字是否按顺序排列。
  3. 然后,替换匹配的那个。
  4. 但这些在实践中不应该是三个不同的步骤。

2 个答案:

答案 0 :(得分:1)

您可以使用preg_replace_callback匹配括号中的数字,然后使用explodearray_map将其转换为整数,然后对其进行排序,然后检查范围。

<强>功能

function fixRange($match) {
    // Extract the numbers
    $numbers = array_map('intval', explode(',', $match[1]));

    // Make sure they're sequential
    sort($numbers);

    $count = count($numbers);

    $replacements = [];

    for ($i = 0; $i < $count; $i++) {
        $startNumber = $endNumber = $numbers[$i];

        for ($j = $i + 1; $j < $count; $j++) {
            if ($numbers[$j] === ($endNumber + 1)) {
                $endNumber = $numbers[$j];

                $i = $j;
            } else {
                break;
            }
        }

        // Add range if $endNumber is not the same as $startNumber
        $replacements[] = $startNumber . ($startNumber !== $endNumber ? '-' . $endNumber : '');
    }

    return '(' . implode(',', $replacements) . ')';
}

示例

<?php

$raw = "outside 1,2,12,13 (5,6,11,12,13,14,16,19,20,21)";

$result = preg_replace_callback('~\(([^\)]+)\)~', 'fixRange', $raw);

var_dump($result);
// string(38) "outside 1,2,12,13 (5-6,11-14,16,19-21)"

$raw = "outside 1,2,12,13 (5,6,11,12,13,14,16,19,20,21,22,23,25)";

$result = preg_replace_callback('~\(([^\)]+)\)~', 'fixRange', $raw);

var_dump($result);
// string(41) "outside 1,2,12,13 (5-6,11-14,16,19-23,25)"

DEMO

答案 1 :(得分:1)

难以阅读但很简短的解决方案(好像这是一种正确的代码编码方式(!))。

$raw = "outside 1,2,3,11,12,13 (5,6,11,12,13,14,16,19,20,21)";

// Find sections inside parentheses
$result = preg_replace_callback("[\(([^\)]+)\)]", function($in_parens){
    // Split the numbers - I'm assuming the format is guaranteed to be just digits and
    // commas.
    $numbers = explode(",", $in_parens[1]);
    return "(" . array_reduce($numbers, function($carry, $number) {
        if (preg_match("[(?:^|[-,])(\d+)$]", $carry, $prev) && $prev[1] == $number - 1) {
            // If the previous number is one less than the current number: remove
            // the previous number (if it's the end of the range) and replace it
            // with "-" followed by this number.
            return preg_replace("[-\d+$]", "", $carry) . "-$number";
        } else {
            // Otherwise just tack this number on to the previous result with a comma.
            return ($carry ? "$carry," : "") . $number;
        }
    }) . ")";
}, $raw);

var_dump($result);
//string 'outside 1,2,3,11,12,13 (5-6,11-14,16,19-21)' (length=43)

此函数用“ - ”标记两个数字范围,尽管您的示例实际上用逗号分隔两个数字范围。