Php 基于括号递归地用逗号分解
想象这样一个字符串:Php 基于括号递归地用逗号分解,php,regex,Php,Regex,想象这样一个字符串: field1,field2(subfield1),field3(subfield2,subfield3),field4(),field5(subfield4(subsubfield,subsubfield2)) array( field1 => array(), field2 => array(subfield1), field3 => array( subfield2, subfield3
field1,field2(subfield1),field3(subfield2,subfield3),field4(),field5(subfield4(subsubfield,subsubfield2))
array(
field1 => array(),
field2 => array(subfield1),
field3 => array(
subfield2,
subfield3
),
field4 => array(),
field5 => array(
subfield4 => array(
subsubfield => array(),
subsubfield => array()
)
)
)
我希望得到如下数组:
field1,field2(subfield1),field3(subfield2,subfield3),field4(),field5(subfield4(subsubfield,subsubfield2))
array(
field1 => array(),
field2 => array(subfield1),
field3 => array(
subfield2,
subfield3
),
field4 => array(),
field5 => array(
subfield4 => array(
subsubfield => array(),
subsubfield => array()
)
)
)
我有一个regex[a-zA-Z0-9]*\([^()]*(?:(?R)[^()]*)*\)
,它完成一些输出工作:
array(
field1,
field2(subfield1),
field3(subfield2,subfield3),
field4(),
field5(subfield4(subsubfield,subsubfield2))
)
虽然这不是我想要的。我现在有点困了,但到目前为止,我提出的选择是:
有人知道我是怎么开始的吗?哪种选择是最好(或更好)的方法?(可读性与资源使用率、复杂性与等等)好的,我认为这就是关键:
$a = "field1,field2(subfield1),field3(subfield2,subfield3),field4(),field5(subfield4(subsubfield,subsubfield2,limit:50,offset:0))";
$output = array();
$outputStacktrace = array(&$output);
$depth = 0;
$buffer = $key = '';
$m = memory_get_usage();
for ($i = 0; $i < strlen($a); $i++)
if ($a[$i] == ':') {
$key = $buffer;
$buffer = '';
} elseif ($a[$i] == ',') {
if (strlen($buffer))
$outputStacktrace[$depth][$key ? $key : count($outputStacktrace[$depth])] = $buffer;
$buffer = $key = '';
} elseif ($a[$i] == '(') {
$outputStacktrace[$depth][$buffer] = array();
$outputStacktrace[$depth + 1] = &$outputStacktrace[$depth][$buffer];
$depth++;
$buffer = '';
} elseif ($a[$i] == ')') {
if (strlen($buffer))
$outputStacktrace[$depth][$key ? $key : count($outputStacktrace[$depth])] = $buffer;
$buffer = $key = '';
unset($outputStacktrace[$depth]);
$depth--;
} else
$buffer .= $a[$i];
var_dump($output);
$a=“字段1,字段2(子字段1),字段3(子字段2,子字段3),字段4(),字段5(子字段4(子字段,子字段2,限制:50,偏移:0))”;
$output=array();
$outputStacktrace=数组(&$output);
$depth=0;
$buffer=$key='';
$m=内存获取使用率();
对于($i=0;$i
都在一个循环中。很满意。非常感谢您的评论
编辑:包括固定变量名、固定最后一个非嵌套字符截断、添加“特殊”查询元素:限制和偏移量
<?php
$output = array();
$outputStack = array(&$output);
$depth = 0;
$buffer = $key = '';
$s = count($a);
for ($i = 0; $i < $s; $i++)
if ($a[$i] == ':') {
$key = $buffer;
$buffer = '';
} elseif ($a[$i] == ',' || $a[$i] == ')' || $i == $s - 1) {
if ($depth < 4) {
if ($i == $s - 1)
$buffer .= $a[$i];
if (strlen($buffer))
if (strlen($key))
if (($key == 'limit' || $key == 'offset') && ((int) $buffer) . "" == $buffer)
$outputStack[$depth][$key] = $buffer;
else
$outputStack[$depth]['filters'][$key] = $buffer;
else
$outputStack[$depth]['fields'][] = $buffer;
}
$buffer = $key = '';
if ($a[$i] == ')') {
array_pop($outputStack);
$depth--;
}
} elseif ($a[$i] == '(') {
if ($depth + 1 < 4) {
$outputStack[$depth]['connections'][$buffer] = array('fields' => array('id'));
$outputStack[$depth + 1] = &$outputStack[$depth]['connections'][$buffer];
}
$depth++;
$buffer = '';
} else
$buffer .= $a[$i];
unset($outputStack, $depth, $buffer, $key, $a, $i);
导言
您描述的问题不能用常规语言表示,因为常规语言无法平衡括号。然而,多年来,大多数正则表达式实现都添加了一些特性,允许解析比正则语言更复杂的语言。特别是,这个问题可以通过.NET的平衡匹配或(感谢@Gumbo在评论中指出这一点)来解决
然而,仅仅因为你能做一件事并不意味着你应该做。在这类任务中使用正则表达式的问题是,随着元语言的扩展,修改正则表达式将变得越来越困难。而解析器往往更具可塑性,更易于扩展
因此,您可能可以构造一系列正则表达式来覆盖输入的非病理性情况,但是,既然您可以编写解析器,为什么还要尝试呢?它们易于维护,速度非常快(比正则表达式快),易于扩展,而且启动起来非常有趣
我最初没有注意到这个问题是在寻找一个PHP解决方案,所以我用JavaScript编写了它。我将其翻译成PHP,并在文章末尾保留了原始JavaScript解决方案
PHP解决方案
函数解析($s){
//我们将始终有一个“当前上下文”。当前上下文就是我们正在使用的数组
//当前在中运行。当我们开始时,这只是一个空数组。作为新数组
//数组创建后,此上下文将更改。
$context=array();
//因为我们必须跟踪上下文的深度,所以我们保留了一个上下文堆栈
$contextStack=数组(&$context);
//这将累加当前数组的名称
$name='';
对于($i=0;$i数组(';
if(arr.children.length){
if(arr.children.length==1&&arr.children[0]。length==0){
php+=arr.children[0]。名称;
}否则{
php+='\n';
缩进+='\t';
对于(var i=0;i,这里有一个简单的解析器可以解决您的问题:
function parse($input) {
preg_match_all('/[,()]|[^,()]+/', $input, $tokens);
// reference to current node list to work with during traversal
$ref = &$output;
// stack to remember node lists
$stack = array();
$output = array();
foreach ($tokens[0] as $token) {
switch ($token) {
case '(':
// push reference to current node list on the stack and
// update reference
$stack[] = &$ref;
$ref = &$ref[$last];
break;
case ')':
// restore previous node list from stack
$ref = &$stack[count($stack)-1];
array_pop($stack);
if (is_null($ref)) echo "error";
break;
case ',':
break;
default:
// insert token into current node list
$ref[$token] = array();
$last = $token;
break;
}
}
if (!empty($stack)) echo "error";
return $ouput
}
$input = 'field1,field2(subfield1),field3(subfield2,subfield3),field4(),field5(subfield4(subsubfield,subsubfield2))';
var_dump(parse($input));
请注意,这只是一个简单的解析器,不能实现所描述语言的完整有限状态自动机,例如,,后面的)
不能存在
但是,如果愿意,您可以添加一些状态机制。只需将每个case
设置为自己的阶段,并检查允许进入新状态的先前状态。您定义自己的数据格式的目的是什么?我只会使用JSON数据结构,这样您就可以使用内置编码器/解码器。如果您需要坚持这一点数据结构我会用递归函数编写我自己的解析器,它使用你的正则表达式。好吧,它不是真正的数据格式。更像是一种超薄的、基于url的查询格式,有点像Facebook Graph API。在这个例子中,重要的是“字段”既可以是一个资源的静态属性,也可以是一个到另一个资源的连接e查询该资源的属性和/或连接。内置的其他东西将是“过滤器”,如限制:50,偏移量:0
。所有对该数组的解析都传递给我的资源对象。我猜这与我的(PHP)方法非常相似。正如你所看到的,Javascript更灵活。我特别喜欢这一行(无论它多么简单):field.parent.children.push(newfield);
PCRE,PHP使用,也可以。哦,好的,@Gumbo!我不知道。我会相应地更新我的答案。Ch