如何从使用print_r打印输出的数组中创建一个新数组?

53

我有一个数组:

$a = array('foo' => 'fooMe');

我做的事情包括:

print_r($a);

打印出如下内容:

Array ( [foo] => printme )

有没有一个函数,可以在执行以下操作时:

needed_function('    Array ( [foo] => printme )');

我会得到数组 array('foo' => 'fooMe'); 吗?


只是为了更多地解释“为什么?”,我也遇到了这个完全相同的问题。以前的开发人员留下了一些代码,从另一个平台解析数据并将结果记录为 print_r 字符串(因为它对人类阅读友好)。一段时间后,需要重新解析这些信息,而唯一的记录是日志。 - Flyingfenix
你可以在这里查看:https://blog.nixarsoft.com/2021/04/16/convert-print_r-result-to-json/ - kodmanyagha
@kodmanyagha,该页面不涉及子数组(遇到Array()时需要递归调用),也不涉及特殊情况。无论如何,print_r()的结果是不确定的,因为键/值可能包含print_r使用的分隔符或\n,而且没有任何转义... - Déjà vu
11个回答

35

我实际上编写了一个函数,将“字符串数组”解析为实际的数组。显然,它有些麻烦并且不太好看,但是它可以在我的测试用例中正常工作。这里有一个链接到可行原型的链接:http://codepad.org/idlXdij3

我也会在内联代码中发布代码,以便那些不想点击链接的人:

<?php
     /**
      * @author ninetwozero
      */
?>
<?php
    //The array we begin with
    $start_array = array('foo' => 'bar', 'bar' => 'foo', 'foobar' => 'barfoo');

    //Convert the array to a string
    $array_string = print_r($start_array, true);

    //Get the new array
    $end_array = text_to_array($array_string);

    //Output the array!
    print_r($end_array);

    function text_to_array($str) {

        //Initialize arrays
        $keys = array();
        $values = array();
        $output = array();

        //Is it an array?
        if( substr($str, 0, 5) == 'Array' ) {

            //Let's parse it (hopefully it won't clash)
            $array_contents = substr($str, 7, -2);
            $array_contents = str_replace(array('[', ']', '=>'), array('#!#', '#?#', ''), $array_contents);
            $array_fields = explode("#!#", $array_contents);

            //For each array-field, we need to explode on the delimiters I've set and make it look funny.
            for($i = 0; $i < count($array_fields); $i++ ) {

                //First run is glitched, so let's pass on that one.
                if( $i != 0 ) {

                    $bits = explode('#?#', $array_fields[$i]);
                    if( $bits[0] != '' ) $output[$bits[0]] = $bits[1];

                }
            }

            //Return the output.
            return $output;

        } else {

            //Duh, not an array.
            echo 'The given parameter is not an array.';
            return null;
        }

    }
?>

不太好用 - 它会删除键和值中的空格!http://codepad.org/GYL4M2xi - Trolley
@Elias 我更新了上面的脚本,不太记得为什么要替换空格。这是一个修订版本的链接(基于你的代码):http://codepad.org/od79B28T - anon
17
对于简单的数组,这个方法可行,但对于多维数组则不适用。只需使用此方法 - machineaddict
我在 $bits[1] 中添加了 trim(),因为它在值周围添加了很多空格。 - phoenix

15
如果您想将数组存储为字符串,请使用serialize[文档]unserialize[文档]
回答您的问题:没有内置函数可以将print_r的输出再次解析为数组。

2
这个答案避免了提供问题所需的解决方案。我们必须假设OP(以及其他找到此页面的研究人员)无法修改输入结构。(如果他们可以修改输入结构,那么SO上有很多其他页面描述如何使用serialize/unserializejson_encode()/json_decode()。) - mickmackusa

9
不可以,但你可以使用serializejson_*函数。
$a = array('foo' => 'fooMe');
echo serialize($a);

$a = unserialize($input);

或者:

echo json_encode($a);

$a = json_decode($input, true);

1
该帖子的提问者并不是在寻求编码/序列化数据的替代方法。该提问者已经说明输入是从print_r()输出的文本,需要对这个文本进行解析。 - mickmackusa

8

1
在这种情况下,“needed_function”是什么? - Felix Kling
这是OP发布的内容... needed_function(' Array ( [foo] => printme )'); - ajreal
2
我认为他想知道这个函数会返回一个数组。 - Felix Kling
2
拜托了,无论如何也不要在您的生产代码中使用 eval - vzwick
1
本帖并不试图解析print_r()生成的文本--这正是OP所需要的。 - mickmackusa

7

有一个很好的在线工具,它正是其名称所描述的:

print_r 转换为 JSON 在线转换器

从 JSON 对象到数组,只需要使用 json_decode 函数即可:

如果想要得到一个数组,可以将第二个参数设置为 true。否则,你将得到一个对象。

json_decode($jsondata, true);

谢谢您提供在线JSON转换器的链接。这正是我所需要的。 - Quixrick

4

我认为我的函数也很酷,可以处理嵌套的数组:

function print_r_reverse($input)
{
    $output = str_replace(['[', ']'], ["'", "'"], $input);
    $output = preg_replace('/=> (?!Array)(.*)$/m', "=> '$1',", $output);
    $output = preg_replace('/^\s+\)$/m', "),\n", $output);
    $output = rtrim($output, "\n,");
    return eval("return $output;");
}

NB: 最好不要在用户输入数据中使用此功能


只要你没有空数组,这个代码就能正常工作,例如:Array ( )一个简单的搜索和替换就可以解决这个限制。 - Tuaris
不处理 stdClass Object、数字数据类型、多行字符串值和转义引号。 - trincot

1
这是一个print_r输出解析器,可以产生与PHP语法相同的表达式。它是作为交互式Stack Snippet编写的,所以您可以在此处使用它:

function parse(s) {
    const quote = s => '"' + s.replace(/["\\]/g, '\\$&') + '"';
    const compress = indent => " ".repeat(((indent.length + 4) >> 3) * 4);
    return "$data = " + (s.replace(/\r\n?/g, "\n") + "\n").replace(
        /(Array|\w+ (Object)) *\n *\( *\n|^( *)(?:\[(?:(0|-?[1-9]\d*)|(.*?))\] => |(\) *\n+))|(-?\d+(?:\.\d+)?(?:E[-+]\d+)?)\n|(.*(?:\n(?! *\) *$| *\[.*?\] => ).*)*)\n/gm,
        (_, array, object, indent, index, key, close, number, string) => 
            object ? "(object) [\n"
                   : array ? "[\n"
                           : close ? compress(indent) + "],\n"
                                   : indent ? compress(indent) + (index ?? quote(key)) + " => "
                                            : (number ?? quote(string)) + ",\n"
    ).replace(/,\n$/, ";");
}

// I/O handling
const [input, output] = document.querySelectorAll("textarea");
(input.oninput = () => output.value = parse(input.value))();
textarea { width: 23em; height: 12em }
<table><tr><th>Input print_r format</th><th>Output PHP syntax</th></tr>
<tr><td><textarea>
Array
(
    [0] => example
    [1] => stdClass Object
        (
            [a[] => 1.43E+19
            ["] => quote
            [] => 
        )
)
</textarea></td><td><textarea readonly></textarea></td></tr></table>

备注

  • 不要从原始的print_r输出中删除任何换行符。例如,Array之后的开括号和闭括号必须分别出现在不同的行上。
  • 不要改变=>周围的间距(前后各一个空格)。
  • 由于print_r不能区分null""false(这些值不会产生输出),也不能区分true1(两者都会输出为1),因此该转换器永远不会产生nullfalsetrue
  • 由于print_r不能区分数字和字符串(9可以表示数字或字符串),所以当存在这种歧义时,该转换器将假定数据类型是数值型
  • stdClass Object得到支持,并翻译为(object) [...]表示法
  • MyClass Object将被视为stdClass对象。
  • 输出中的字符串字面量使用双引号,字面双引号和反斜杠被转义。

0
这是我理解问题的方式:
function parsePrintedArray($s){
    $lines = explode("\n",$s);
    $a = array();
    foreach ($lines as $line){
        if (strpos($line,"=>") === false)
            continue;
        $parts = explode('=>',$line);
        $a[trim($parts[0],'[] ')] = trim($parts[1]);
    }
    return $a;
}

适用于对象和数组:

$foo = array (
    'foo' => 'bar',
    'cat' => 'dog'
);

$s = print_r($foo,1);
$a = parsePrintedArray($s);
print_r($a);

输出:

Array
(
    [foo] => bar
    [cat] => dog
) 

不支持嵌套数组,但简单快速。


不处理嵌套数组,stdClass Object(它返回一个数组而不是对象),数字数据类型,多行字符串值,以及以空格开头/结尾的字符串。 - trincot

0

快速函数(不检查您是否发送了正确的数据):

function textToArray($str)
{
    $output = [];

    foreach (explode("\n", $str) as $line) {

        if (trim($line) == "Array" or trim($line) == "(" or trim($line) == ")") {
            continue;
        }

        preg_match("/\[(.*)\]\ \=\>\ (.*)$/i", $line, $match);

        $output[$match[1]] = $match[2];
    }

    return $output;
}

这是预期的输入:

Array
(
    [test] => 6
)

1
不适用于嵌套数组。 - trincot
如果您的模式中没有字母,那么不区分大小写的模式修饰符有什么意义呢?在这个答案中需要进行太多不必要的转义。 - mickmackusa

-1

使用

var_export(array('Sample array', array('Apple', 'Orange')));

输出:

array (
  0 => 'Sample array',
  1 => 
  array (
    0 => 'Apple',
    1 => 'Orange',
  ),
)

1
本帖子绝不试图解析print_r()生成的文本。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接