0

由于我习惯了 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 函数来执行此操作之前,有没有人知道现有函数或进行上述转换的一些聪明方法?

更新:

@glennjackman 指出我的例子是不一致的,确实如此。下的第三个实体key4应该是:

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

然而不幸的是,这不是我想象的输出。经过进一步思考,我认为为了得到我想要的,有必要引入一种交替可区分的方式来指示文字,例如使用双引号"",或者使用 a 后面的空白{作为规则来解释为文字。

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

4

3 回答 3

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;
}
于 2012-10-28T03:34:17.190 回答
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是正确的,因为一旦}找到后跟空格,文字就会关闭。因此,以下}s 被作为数组的结尾处理,导致提前终止,并留下部分输入字符串未处理:

        }
        {
            e{f
            g}h
            ij{
            }kl
            mn}
        {
            {op}}
        {qrs
于 2012-10-28T08:53:48.953 回答
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 个单词等?

于 2012-10-28T01:51:33.400 回答