将Tcl列表(或类似)解析为PHP数组

时间:2012-10-27 22:49:23

标签: php multidimensional-array tcl

由于我习惯于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"
    ),
)

由程序知道将blockkey3的内容读作键值对,key4作为项目数组(折叠嵌套括号),等等上。

我不一定要使用花括号({}) - 例如,虽然我对Lisp一无所知,但它似乎使用括号(())(并且看起来它影响了Tcl-我确定我在这里错过了很多相关的历史......),这很好。

认为上面的例子是一致的,但我不确定。我认为规则是,“如果没有空格(除了前导和尾随),则将其视为单个文字实体;否则,视为数组。”

  1. 此类数据是否有官方用语,所有内容都是列表?

  2. 在我开始编写PHP函数之前,是否有人知道现有函数或执行上述转换的一些聪明方法?

  3. 更新

    @glennjackman指出我的例子不一致,就是这样。 key4下的第三个实体应为:

            [2] => (
                [0] => "item3",
                [1] => "has",
                [2] => "spaces",
                [3] => "in",
                [4] => "it",
            ),
    

    不幸的是,这不是我想象的那样的输出。在进一步思考之后,我认为为了得到我想要的东西,必要引入一种交替可区分的表示文字的方式,例如使用双引号"",或者使用{之后缺少空格来解释为文字。

    现在,我会选择后者,直到我想到一个更优雅的解决方案。也就是说,如果开放式大括号{后面紧跟非空白字符,则考虑开括号的所有内容,即文字字符串。

3 个答案:

答案 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个单词等?