用不在潜在嵌套括号内的逗号分隔字符串

问题描述 投票:0回答:9

两天前,我开始研究代码解析器,但我陷入了困境。

如何用不在括号内的逗号分隔字符串?让我告诉你我的意思。

我有这个字符串要解析:

one, two, three, (four, (five, six), (ten)), seven

我想得到这个结果:

array(
 "one"; 
 "two"; 
 "three"; 
 "(four, (five, six), (ten))"; 
 "seven"
)

但我得到的是:

array(
  "one"; 
  "two"; 
  "three"; 
  "(four"; 
  "(five"; 
  "six)"; 
  "(ten))";
  "seven"
)

如何在 PHP RegEx 中执行此操作。

php regex split text-parsing delimited
9个回答
13
投票

您可以更轻松地做到这一点:

preg_match_all('/[^(,\s]+|\([^)]+\)/', $str, $matches)

但是如果你使用真正的解析器会更好。也许是这样的:

$str = 'one, two, three, (four, (five, six), (ten)), seven';
$buffer = '';
$stack = array();
$depth = 0;
$len = strlen($str);
for ($i=0; $i<$len; $i++) {
    $char = $str[$i];
    switch ($char) {
    case '(':
        $depth++;
        break;
    case ',':
        if (!$depth) {
            if ($buffer !== '') {
                $stack[] = $buffer;
                $buffer = '';
            }
            continue 2;
        }
        break;
    case ' ':
        if (!$depth) {
            continue 2;
        }
        break;
    case ')':
        if ($depth) {
            $depth--;
        } else {
            $stack[] = $buffer.$char;
            $buffer = '';
            continue 2;
        }
        break;
    }
    $buffer .= $char;
}
if ($buffer !== '') {
    $stack[] = $buffer;
}
var_dump($stack);

6
投票

嗯...好的,已经标记为已回答,但既然你要求一个简单的解决方案,我仍然会尝试:

$test = "one, two, three, , , ,(four, five, six), seven, (eight, nine)";
$split = "/([(].*?[)])|(\w)+/";
preg_match_all($split, $test, $out);
print_r($out[0]);              

输出

Array
(
    [0] => one
    [1] => two
    [2] => three
    [3] => (four, five, six)
    [4] => seven
    [5] => (eight, nine)
)

4
投票

你不能直接。 你至少需要可变宽度的lookbehind,最后我知道PHP的PCRE只有固定宽度的lookbehind。

我的第一个建议是首先从字符串中提取带括号的表达式。 不过,我对您的实际问题一无所知,所以我不知道这是否可行。


2
投票

我想不出使用单个正则表达式来完成此操作的方法,但将有效的东西组合在一起非常容易:

function process($data)
{
        $entries = array();
        $filteredData = $data;
        if (preg_match_all("/\(([^)]*)\)/", $data, $matches)) {
                $entries = $matches[0];
                $filteredData = preg_replace("/\(([^)]*)\)/", "-placeholder-", $data);
        }

        $arr = array_map("trim", explode(",", $filteredData));

        if (!$entries) {
                return $arr;
        }

        $j = 0;
        foreach ($arr as $i => $entry) {
                if ($entry != "-placeholder-") {
                        continue;
                }

                $arr[$i] = $entries[$j];
                $j++;
        }

        return $arr;
}

如果你像这样调用它:

$data = "one, two, three, (four, five, six), seven, (eight, nine)";
print_r(process($data));

它输出:

Array
(
    [0] => one
    [1] => two
    [2] => three
    [3] => (four, five, six)
    [4] => seven
    [5] => (eight, nine)
)

2
投票

也许有点晚了,但我已经做了一个没有正则表达式的解决方案,它也支持嵌套在括号内。大家有什么想法请告诉我:

$str = "Some text, Some other text with ((95,3%) MSC)";
$arr = explode(",",$str);

$parts = [];
$currentPart = "";
$bracketsOpened = 0;
foreach ($arr as $part){
    $currentPart .= ($bracketsOpened > 0 ? ',' : '').$part;
    if (stristr($part,"(")){
        $bracketsOpened ++;
    }
    if (stristr($part,")")){
        $bracketsOpened --;                 
    }
    if (!$bracketsOpened){
        $parts[] = $currentPart;
        $currentPart = '';
    }
}

给我输出:

Array
(
    [0] => Some text
    [1] =>  Some other text with ((95,3%) MSC)
)

1
投票

笨拙,但它确实有效......

<?php

function split_by_commas($string) {
  preg_match_all("/\(.+?\)/", $string, $result); 
  $problem_children = $result[0];
  $i = 0;
  $temp = array();
  foreach ($problem_children as $submatch) { 
    $marker = '__'.$i++.'__';
    $temp[$marker] = $submatch;
    $string   = str_replace($submatch, $marker, $string);  
  }
  $result = explode(",", $string);
  foreach ($result as $key => $item) {
    $item = trim($item);
    $result[$key] = isset($temp[$item])?$temp[$item]:$item;
  }
  return $result;
}


$test = "one, two, three, (four, five, six), seven, (eight, nine), ten";

print_r(split_by_commas($test));

?>

1
投票

我觉得值得注意的是,您应该尽可能避免使用正则表达式。为此,您应该知道,对于 PHP 5.3+,您可以使用 str_getcsv()。但是,如果您正在处理文件(或文件流),例如 CSV 文件,那么函数 fgetcsv() 可能就是您所需要的,并且它从 PHP4 起就可用。

最后,我很惊讶没有人使用preg_split(),或者它没有按需要工作?


0
投票

恐怕解析嵌套括号会非常困难,例如

one, two, (three, (four, five))
仅适用于正则表达式。


0
投票

这个更正确,但仍然不适用于嵌套括号

/[^(,]*(?:([^)]+))?[^),]*/

– DarkSide 2013 年 3 月 24 日 23:09

您的方法无法解析“一,二,三,((五),(四(六))),七,八,九”。我认为正确的正则表达式是递归的:/(([^()]+|(?R))*)/。
– 克里斯蒂安·托马 2009 年 7 月 6 日 7:26

是的,它更容易,但在嵌套括号的情况下不起作用,如下所示:一,二,三,(四,(五,六),(十)),七
– 克里斯蒂安·托马 2009 年 7 月 6 日 7:41

非常感谢您,非常感谢您的帮助。但现在我意识到我也会遇到嵌套括号,并且您的解决方案不适用。
– 克里斯蒂安·托马 2009 年 7 月 6 日 7:43

在我看来,我们需要一个尊重平衡括号分组的字符串分割算法。 我将使用递归正则表达式模式来解决这个问题! 该行为将尊重最低的平衡括号,并让任何更高级别的不平衡括号被视为非分组字符。 请对任何未正确分割的输入字符串发表评论,以便我可以尝试进行改进(测试驱动开发)。

代码:(演示

$tests = [
    'one, two, three, (four, five, six), seven, (eight, nine)',
    '()',
    'one and a ),',
    '(one, two, three)',
    'one, (, two',
    'one, two, ), three',
    'one, (unbalanced, (nested, whoops ) two',
    'one, two, three and a half, ((five), (four(six))), seven, eight, nine',
    'one, (two, (three and a half, (four, (five, (six, seven), eight)))), nine, (ten, twenty twen twen)',
    'ten, four, (,), good buddy',
];

foreach ($tests as $test) {
    var_export(
        preg_split(
            '/(?>(\((?:(?>[^()]+)|(?1))*\))|[^,]+)\K,?\s*/',
            $test,
            0,
            PREG_SPLIT_NO_EMPTY
        )
    );
    echo "\n";
}

输出:

array (
  0 => 'one',
  1 => 'two',
  2 => 'three',
  3 => '(four, five, six)',
  4 => 'seven',
  5 => '(eight, nine)',
)
array (
  0 => '()',
)
array (
  0 => 'one and a )',
)
array (
  0 => '(one, two, three)',
)
array (
  0 => 'one',
  1 => '(',
  2 => 'two',
)
array (
  0 => 'one',
  1 => 'two',
  2 => ')',
  3 => 'three',
)
array (
  0 => 'one',
  1 => '(unbalanced',
  2 => '(nested, whoops )',
  3 => 'two',
)
array (
  0 => 'one',
  1 => 'two',
  2 => 'three and a half',
  3 => '((five), (four(six)))',
  4 => 'seven',
  5 => 'eight',
  6 => 'nine',
)
array (
  0 => 'one',
  1 => '(two, (three and a half, (four, (five, (six, seven), eight))))',
  2 => 'nine',
  3 => '(ten, twenty twen twen)',
)
array (
  0 => 'ten',
  1 => 'four',
  2 => '(,)',
  3 => 'good buddy',
)

这是一个相关的答案,它递归地遍历括号组并反转每个级别上逗号分隔值的顺序:反转括号分组文本的顺序并反转括号组的顺序

© www.soinside.com 2019 - 2024. All rights reserved.