【问题标题】:Explode complex string by commas in PHP在 PHP 中用逗号分解复杂字符串
【发布时间】:2012-06-16 09:22:47
【问题描述】:

我需要拆分一个包含逗号的字符串。我已经为 (str_getcsv) 之类的字符串找到了一些东西:

'A', 'B with a comma, eh', 'C'

但我的字符串是这样的,例如值没有封闭字符:

A, B (one, two), C

我需要炸掉这个并获得:

array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(12) "B (one, two)"
  [2]=>
  string(1) "C"
}

我想使用括号内 not 的逗号来拆分字符串,因为这是我的唯一一种情况,爆炸失败

【问题讨论】:

  • 正如你所描述的,你的问题归结为“我想用魔法分割这个字符串,因为对于我作为一个人来说,括号中的逗号显然不应该分割它” .你应该比这个单一的例子更好地定义你的分割标准。
  • Foo ((bar), baz) 这样的案例怎么样?如果您期望嵌套的括号,则解析会比您期望只有一个级别的括号复杂得多。
  • @lanzz:不,没有嵌套括号。

标签: php regex string split


【解决方案1】:

但是你的疯狂愿望有一个解决方案;)

$a = "(Z) X, (Y, W) A, B (one, two), C, D (E,F,G) H, I J";
$reg = '/[^(,]*(?:\([^)]+\))?[^),]*/';
preg_match_all($reg, $a, $matches);
$result = array_filter($matches[0]);
var_dump($result);

【讨论】:

  • 现在升级:如果 $a 是 "(a) b, c" 怎么办? :-) 第一个括号被跳过。
【解决方案2】:

这个 sn-p 可以帮助我处理嵌套括号。基本上想法是用一些标识符递归地替换 (*) 直到没有更多的括号。然后用逗号分解字符串,然后将所有内容放回原处。 这不是理想的解决方案 - 刚刚在大约 30 分钟内完成,但它有效 :) 它肯定可以通过某种方式进行优化。

/**
 * Explode string by delimiter, but don't explode if delimiter is inside parenthesis.
 * This also support nested parenthesis - that's where pure RegExp solutions fails.
 * 
 * For example,
 *  $input = "one, two three, four (five, (six, seven), (eight)) (nine, ten), eleven";
 *  $output = array(
 *      'one',
 *      'two three',
 *      'four (five, (six, seven), (eight)) (nine, ten)',
 *      'eleven'
 *  );
 * 
 * @param string $input
 * @param string $delimiter = ,
 * @param string $open_tag = \(
 * @param string $close_tag = \)
 * @return array
 */
function exploder($input, $delimiter = ',', $open_tag = '\(', $close_tag = '\)')
{
    // this will match any text inside parenthesis
    // including parenthesis itself and without nested parenthesis
    $regexp = '/'.$open_tag.'[^'.$open_tag.$close_tag.']*'.$close_tag.'/';

    // put in placeholders like {{\d}}. They can be nested.
    $r = array();
    while (preg_match_all($regexp, $input, $matches)) {
        if ($matches[0]) {
            foreach ($matches[0] as $match) {
                $r[] = $match;
                $input = str_replace($match, '{{'.count($r).'}}', $input);
            }
        } else {
            break;
        }
    }
    $output = array_map('trim', explode($delimiter, $input));

    // put everything back
    foreach ($output as &$a) {
        while (preg_match('/{{(\d+)}}/', $a, $matches)) {
            $a = str_replace($matches[0], $r[$matches[1] - 1], $a);
        }
    }

    return $output;
}

$a = "one, two three, four (five, (six, seven), (eight)) (nine, ten), eleven";
var_dump(exploder($a));

这将输出:

array (size=4)
  0 => string 'one' (length=3)
  1 => string 'two three' (length=9)
  2 => string 'four (five, (six, seven), (eight)) (nine, ten)' (length=46)
  3 => &string 'eleven' (length=6)

正如预期的那样。

【讨论】:

    【解决方案3】:

    比创建一个数组然后过滤结果更优雅,你可以在这个单函数单行中使用preg_split()

    代码:(Demo)

    $string='A, B (one, two), C';
    var_export(preg_split('/(?:\([^)]*\)(*SKIP)(*FAIL))|, /',$string));
    

    输出:

    array (
      0 => 'A',
      1 => 'B (one, two)',
      2 => 'C',
    )
    

    Pattern Demo

    • (*SKIP)(*FAIL) 是一种在匹配之前取消子字符串资格的技术。
    • 否定字符类[^)]*.(点)的更快替代方案。 *如果您有嵌套的括号表达式,则此模式将不起作用...为该场景编写模式有点超出此问题的范围。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-28
      • 2021-11-26
      • 2021-04-21
      • 1970-01-01
      • 1970-01-01
      • 2014-11-10
      • 2023-03-29
      相关资源
      最近更新 更多