在PHP中用逗号分解复杂的字符串

时间:2012-06-16 09:22:47

标签: php regex string split

我需要拆分包含逗号的字符串。我已经找到像(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 逗号来分割字符串,因为这是我爆炸失败时唯一的情况

3 个答案:

答案 0 :(得分:5)

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

$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);

答案 1 :(得分:1)

这个片段帮助我使用嵌套括号。基本上,想法是用一些标识符递归替换(*),直到没有更多的括号。然后用逗号分解字符串,然后将所有内容放回去。 这不是理想的解决方案 - 现在大约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)

正如所料。

答案 2 :(得分:0)

比创建数组然后过滤结果更优雅,您可以在这个单功能单行中使用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)是一种在匹配前取消子字符串资格的技术。
  • 否定字符类[^)]*.(点)的更快替代品。 *如果你有嵌套的括号表达式,这个模式将不起作用...为该场景编写一个模式有点超出了这个问题的范围。