两天前,我开始研究代码解析器,但我陷入了困境。
如何用不在括号内的逗号分隔字符串?让我告诉你我的意思。
我有这个字符串要解析:
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 中执行此操作。
您可以更轻松地做到这一点:
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);
嗯...好的,已经标记为已回答,但既然你要求一个简单的解决方案,我仍然会尝试:
$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)
)
你不能直接。 你至少需要可变宽度的lookbehind,最后我知道PHP的PCRE只有固定宽度的lookbehind。
我的第一个建议是首先从字符串中提取带括号的表达式。 不过,我对您的实际问题一无所知,所以我不知道这是否可行。
我想不出使用单个正则表达式来完成此操作的方法,但将有效的东西组合在一起非常容易:
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)
)
也许有点晚了,但我已经做了一个没有正则表达式的解决方案,它也支持嵌套在括号内。大家有什么想法请告诉我:
$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)
)
笨拙,但它确实有效......
<?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));
?>
我觉得值得注意的是,您应该尽可能避免使用正则表达式。为此,您应该知道,对于 PHP 5.3+,您可以使用 str_getcsv()。但是,如果您正在处理文件(或文件流),例如 CSV 文件,那么函数 fgetcsv() 可能就是您所需要的,并且它从 PHP4 起就可用。
最后,我很惊讶没有人使用preg_split(),或者它没有按需要工作?
恐怕解析嵌套括号会非常困难,例如
one, two, (three, (four, five))
仅适用于正则表达式。
这个更正确,但仍然不适用于嵌套括号
/[^(,]*(?:([^)]+))?[^),]*/
– 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',
)
这是一个相关的答案,它递归地遍历括号组并反转每个级别上逗号分隔值的顺序:反转括号分组文本的顺序并反转括号组的顺序