由于我习惯于Tcl而且喜欢它的简单性,我想创建和解析表单的配置文件,
block
{
key1 val1
key2 val2
key3
{
subkey1 subval1
subkey2 subval2
}
key4
{
item1
item2
{item3 has spaces in it}
{item4 {has {bra{ck}ets}} in it}
}
}
上面例子中我期望的PHP数组是:
[0] => "block",
[1] => (
[0] => "key1",
[1] => "val1",
[2] => "key2",
[3] => "val2",
[4] => "key3",
[5] => (
[0] => "subkey1",
[1] => "subval1",
[2] => "subkey2",
[3] => "subval2"
),
[6] => "key4",
[7] => (
[0] => "item1",
[1] => "item2",
[2] => "item3 has spaces in it"
[3] => (
[0] => "item4",
[1] => (
[0] => "has",
[1] => "bra{ck}ets"
),
[2] => "in",
[3] => "it"
),
)
由程序知道将block
和key3
的内容读作键值对,key4
作为项目数组(折叠嵌套括号),等等上。
我不一定要使用花括号({}
) - 例如,虽然我对Lisp一无所知,但它似乎使用括号(()
)(并且看起来它影响了Tcl-我确定我在这里错过了很多相关的历史......),这很好。
我认为上面的例子是一致的,但我不确定。我认为规则是,“如果没有空格(除了前导和尾随),则将其视为单个文字实体;否则,视为数组。”
此类数据是否有官方用语,所有内容都是列表?
在我开始编写PHP函数之前,是否有人知道现有函数或执行上述转换的一些聪明方法?
更新
@glennjackman指出我的例子不一致,就是这样。 key4
下的第三个实体应为:
[2] => (
[0] => "item3",
[1] => "has",
[2] => "spaces",
[3] => "in",
[4] => "it",
),
不幸的是,这不是我想象的那样的输出。在进一步思考之后,我认为为了得到我想要的东西,必要引入一种交替可区分的表示文字的方式,例如使用双引号""
,或者使用{
之后缺少空格来解释为文字。
现在,我会选择后者,直到我想到一个更优雅的解决方案。也就是说,如果开放式大括号{
后面紧跟非空白字符,则考虑开括号的所有内容,即文字字符串。
答案 0 :(得分:2)
这是一个解决方案... 我相信它可以改进 ..你的格式让我很头疼但是我能够在一段时间后破解它
使用的代码
$string = 'block
{
key1 val1
key2 val2
key3
{
subkey1 subval1
subkey2 subval2
}
key4
{
item1
item2
{item3 has spaces in it}
{item4 {has {bra{ck}ets}} in it}
}
key5
{
This
{
is
{
just
{Too Crazy {format}}
}
}
}
}';
修改前的格式
echo "<pre>";
print_r(parseTCL($string));
输出
Array
(
[block] => Array
(
[0] => key1
[1] => val1
[2] => key2
[3] => val2
[key3] => Array
(
[0] => subkey1
[1] => subval1
[2] => subkey2
[3] => subval2
)
[key4] => Array
(
[0] => item1
[1] => item2
[2] => item3 has spaces in it <--- Item 3 not broken
[item4] => Array
(
[0] => has
[1] => bra{ck}ets
)
[3] => in
[4] => it
)
[key5] => Array
(
[This] => Array
(
[is] => Array
(
[0] => just
[1] => Too
[Crazy] => Array
(
[0] => format
)
)
)
)
)
)
编辑后的格式
echo "<pre>";
print_r(parseTCL($string,true));
^----------- Additional Option included
输出
.....
[key4] => Array
(
[0] => item1
[1] => item2
[2] => item3 <---------- Item 3 has been broken
[3] => has
[4] => spaces
[5] => in
[item4] => Array
(
[0] => has
[1] => bra{ck}ets
)
[6] => in
[7] => it
)
.....
使用的功能
function parseTCL($string, $breakCurly = false) {
$dataArray = $paths = $toks = $final = array();
$path = $last = "";
/**
* Prepare Tokens
*/
$array = array_map("trim", explode("\n", $string));
foreach ( new ArrayIterator($array) as $value ) {
if (strpos($value, " {") !== false) {
$v = trim($value, " {}");
$v = str_replace(array(" {","} "), array(" \n{","\n}\n"), $v);
$v = explode("\n", $v);
foreach ( $v as $n ) {
if (strpos($n, "{") !== false && strpos($n, "}") !== false) {
$toks[] = $n;
continue;
} else if (strpos($n, "{") !== false) {
$toks[] = "{";
$toks[] = trim($n, "{");
} else if (strpos($n, "}") !== false) {
$toks[] = "}";
$toks[] = trim($n, "}");
} else {
if (strpos($n, " ") !== FALSE) {
$v = explode(" ", $n);
foreach ( $v as $n ) {
$toks[] = $n;
}
} else {
$toks[] = $n;
}
}
}
continue;
}
if (strpos($value, " ") !== FALSE && (strpos($value, "{") !== 0 || $breakCurly == true)) {
$breakCurly === true AND $value = trim($value,"{}");
$v = explode(" ", $value);
foreach ( $v as $n ) {
$toks[] = $n;
}
continue;
}
$toks[] = $value;
}
unset($array);
/**
* Convert Tokens to Paths
*/
foreach ( new ArrayIterator($toks) as $tok ) {
$tok = trim($tok);
if (empty($tok))
continue;
if ($tok == "{") {
$path .= $last . "/";
continue;
}
if ($tok == "}") {
$path = substr($path, 0, strrpos(trim($path, "/"), "/")) . "/";
continue;
}
$tok = trim($tok, "{}");
$paths[] = $path . $tok;
$last = $tok;
}
/**
* Convert PATH To array
*/
$cit = new CachingIterator(new ArrayIterator($paths));
foreach ( $cit as $path ) {
if (empty($path))
continue;
if ($cit->hasNext()) {
$in = $cit->getInnerIterator()->current();
if (strpos($in, $path) === 0)
continue;
}
$parts = array_filter(explode("/", $path));
$value = array_pop($parts);
$temp = &$dataArray;
foreach ( $parts as $key ) {
$temp = &$temp[$key];
}
$temp[] = $value;
}
unset($paths);
return $dataArray;
}
答案 1 :(得分:1)
花了一段时间,但我提出了一个解决方案(不使用正则表达式)。
function list_to_array(&$str, $detect_literals = false)
{
$arr = array();
while ($str = ltrim($str))
{
if ($str[0] === '{')
{
if (!$detect_literals || ctype_space($str[1]))
{
$str = substr($str, 1);
$arr[] = list_to_array($str, $detect_literals);
}
else
{
$pos = -1;
do $pos = strpos($str, '}', $pos+1);
while ($pos && !ctype_space($str[$pos+1]));
if (!$pos) $pos = strlen($str);
while ($str[$pos-1] === '}') $pos--;
$arr[] = substr($str, 1, $pos-1);
$str = substr($str, $pos+1);
}
}
elseif ($str[0] === '}' && ctype_space(substr(ltrim($str, '}'), 0, 1)))
{
$str = substr($str, 1);
return $arr;
}
else
{
$pos = strlen(strtok($str, " \t\n\r\0\x0B"));
while ($str[$pos-1] === '}') $pos--;
$arr[] = substr($str, 0, $pos);
$str = substr($str, $pos);
}
}
return $arr;
}
其中detect_literals
默认为false
,并将所有{...}
扩展为数组,而不是true
并仅展开{ ...}
(请注意空格)作为数组,否则作为文字。
这是原始问题的输入字符串:
$str = '
block
{
key1 val1
key2 val2
key3
{
subkey1 subval1
subkey2 subval2
}
key4
{
item1
item2
{item3 has spaces in it}
{item4 {has {bra{ck}ets}} in it}
}
}
';
Array
(
[0] => block
[1] => Array
(
[0] => key1
[1] => val1
[2] => key2
[3] => val2
[4] => key3
[5] => Array
(
[0] => subkey1
[1] => subval1
[2] => subkey2
[3] => subval2
)
[6] => key4
[7] => Array
(
[0] => item1
[1] => item2
[2] => Array
(
[0] => item3
[1] => has
[2] => spaces
[3] => in
[4] => it
)
[3] => Array
(
[0] => item4
[1] => Array
(
[0] => has
[1] => Array
(
[0] => bra{ck}ets
)
)
[2] => in
[3] => it
)
)
)
)
Array
(
[0] => block
[1] => Array
(
[0] => key1
[1] => val1
[2] => key2
[3] => val2
[4] => key3
[5] => Array
(
[0] => subkey1
[1] => subval1
[2] => subkey2
[3] => subval2
)
[6] => key4
[7] => Array
(
[0] => item1
[1] => item2
[2] => item3 has spaces in it
[3] => item4 {has {bra{ck}ets
)
[8] => in
[9] => it
)
)
请注意,[3] => item4 {has {bra{ck}ets
是正确的,因为检测文字的规则是:(1)开放式大括号后跟非空白字符和(2)第一个闭式大括号之间的所有内容通过空格字符,即文字中的开括号将被忽略。
为了测试健壮性我也尝试了以下字符串:
$str = '
a
{
}
{}
{}{}
{
b
{
}
}
{
{}
c
}
{
{ {{ {d}} }}
}
{
e{f
g}h
ij{
}kl
mn}
{
{op}}
{qrs
';
Array
(
[0] => a
[1] => Array
(
)
[2] => Array
(
)
[3] => Array
(
[0] => }{
)
[4] => Array
(
[0] => b
[1] => Array
(
)
)
[5] => Array
(
[0] => Array
(
)
[1] => c
)
[6] => Array
(
[0] => Array
(
[0] => Array
(
[0] => Array
(
[0] => Array
(
[0] => d
)
)
)
)
)
[7] => Array
(
[0] => e{f
[1] => g}h
[2] => ij{
[3] => }kl
[4] => mn
)
[8] => Array
(
[0] => Array
(
[0] => op
)
)
[9] => Array
(
[0] => qrs
)
)
Array
(
[0] => a
[1] => Array
(
)
[2] =>
[3] => }{
[4] => Array
(
[0] => b
[1] => Array
(
)
)
[5] => Array
(
[0] =>
[1] => c
)
[6] => Array
(
[0] => Array
(
[0] => { {d
)
)
)
请注意[0] => { {d
是正确的,因为只要找到}
后跟空格,就会关闭文字。因此,以下}
被处理为数组的末尾,导致提前终止,并且未对输入字符串的一部分进行处理:
}
{
e{f
g}h
ij{
}kl
mn}
{
{op}}
{qrs
答案 2 :(得分:0)
不是答案,但我希望格式比评论更多
这是一个难题。当你看到
item2
{item3 has spaces in it}
你如何决定制作
[1] => "item2",
[2] => "item3 has spaces in it"
而不是
[1] => "item2",
[2] => (
[0] => "item3",
[1] => "has"
[2] => "spaces"
[3] => "in"
[4] => "it"
),
...
您是否依赖于文档的结构来确定列表是真的是列表还是只是字符串?它是否存在换行符,或者一行中只有2个单词等?