PHP:字符串转换为多维数组

5

非常抱歉我的英语不好。

我有一个字符串,想把它分割成一个数组。 尖括号是多层嵌套的数组。 转义字符应该被保留。

这是一个示例字符串:

$string = '[[["Hello, \"how\" are you?","Good!",,,123]],,"ok"]'

结果结构应该如下所示:
array (
  0 => 
  array (
    0 => 
    array (
      0 => 'Hello, \"how\" are you?',
      1 => 'Good!',
      2 => '',
      3 => '',
      4 => '123',
    ),
  ),
  1 => '',
  2 => 'ok',
)

我已经用以下方式进行了测试:

$pattern = '/[^"\\]*(?:\\.[^"\\]*)*/s';
$return = preg_match_all($pattern, $string, null);

但是这并没有正常工作。我不理解这些正则表达式模式(我在本页面的另一个示例中发现了这个)。 我不知道preg_match_all是否是正确的命令。

希望有人能帮助我。

非常感谢!!!

3个回答

2

这是一个针对正则表达式比较棘手的问题 - 但是有一个hack方法可以回答你的问题(提前道歉)。

该字符串几乎是有效的数组字面量,但存在,,。您可以匹配这些成对的逗号并将其转换为,'' ,使用

/,(?=,)/

然后,您可以将该字符串eval成您要查找的输出数组。

例如:

// input 
$str1 = '[[["Hello, \\"how\\" are you?","Good!",,,123]],,"ok"]';

// replace , followed by , with ,'' with a regex
$pattern = '/,(?=,)/';
$replace = ",''";
$str2 = preg_replace($pattern, $replace, $str1);

// eval updated string
$arr = eval("return $str2;");
var_dump($arr);

我理解为:

array(3) {
  [0]=>
  array(1) {
    [0]=>
    array(5) {
      [0]=>
      string(21) "Hello, "how" are you?"
      [1]=>
      string(5) "Good!"
      [2]=>
      string(0) ""
      [3]=>
      string(0) ""
      [4]=>
      int(123)
    }
  }
  [1]=>
  string(0) ""
  [2]=>
  string(2) "ok"
}

编辑

考虑到 eval 的潜在危险,更好的选择是使用上面的代码和 json_decode,例如:

// input 
$str1 = '[[["Hello, \\"how\\" are you?","Good!",,,123]],,"ok"]';

// replace , followed by , with ,'' with a regex
$pattern = '/,(?=,)/';
$replace = ',""';
$str2 = preg_replace($pattern, $replace, $str1);

// eval updated string
$arr = json_decode($str2);
var_dump($arr);

1
使用eval是一种危险的方式,因为您不知道字符串中包含什么。用,(?=,)替换为''不能处理引号之间有,,的情况。我担心唯一的方法是逐个字符解析字符串或尝试将其转换为一个众所周知的格式,如JSON。但这不是一项容易的任务,虽然不是不可能。 - Casimir et Hippolyte
你是正确的 - 我已经为问题中的hack答案道歉了。我相信json_encode,,问题存在相同的挑战。 - Robin Mackenzie
抱歉 - json_decode - 如果将 ,, 替换为 "",则此代码将正常工作,例如 var_dump(json_decode('[[["Hello, \"how\" are you?","Good!","","",123]],"","ok"]')); - Robin Mackenzie
1
您可以使用以下代码解决 ,, 问题:$string = preg_replace('~"[^"\\\\]*+(?s:\\\\.[^"\\\\]*)*+"?(*SKIP)(*F)|,\K(?=[],])|\[\K(?=,)~', '""',$string); (该代码还处理了 [,,] 等情况,并避免了引号字符串。) - Casimir et Hippolyte

1
如果您可以编辑序列化数据的代码,那么最好使用json_encode和json_decode来处理序列化。这个问题不需要重新发明轮子。

顺便说一下,猫很可爱。


0
你可能想要使用词法分析器与递归函数结合来构建结构。
为了实现你的目的,以下标记已被使用:
\[           # opening bracket
\]           # closing bracket
".+?(?<!\\)" # " to ", making sure it's not escaped
,(?!,)       # a comma, not followed by a comma
\d+          # at least one digit
,(?=,)       # a comma followed by a comma

The rest is programming logic, see a demo on ideone.com. Inspired by this post.


class Lexer {
    protected static $_terminals = array(
        '~^(\[)~'               => "T_OPEN",
        '~^(\])~'               => "T_CLOSE",
        '~^(".+?(?<!\\\\)")~'   => "T_ITEM",
        '~^(,)(?!,)~'           => "T_SEPARATOR",
        '~^(\d+)~'              => "T_NUMBER",
        '~^(,)(?=,)~'           => "T_EMPTY"
    );

    public static function run($line) {
        $tokens = array();
        $offset = 0;
        while($offset < strlen($line)) {
            $result = static::_match($line, $offset);
            if($result === false) {
                throw new Exception("Unable to parse line " . ($line+1) . ".");
            }
            $tokens[] = $result;
            $offset += strlen($result['match']);
        }
        return static::_generate($tokens);
    }

    protected static function _match($line, $offset) {
        $string = substr($line, $offset);

        foreach(static::$_terminals as $pattern => $name) {
            if(preg_match($pattern, $string, $matches)) {
                return array(
                    'match' => $matches[1],
                    'token' => $name
                );
            }
        }
        return false;
    }

    // a recursive function to actually build the structure
    protected static function _generate($arr=array(), $idx=0) {
        $output = array();
        $current = 0;
        for($i=$idx;$i<count($arr);$i++) {
            $type = $arr[$i]["token"];
            $element = $arr[$i]["match"];
            switch ($type) {
                case 'T_OPEN':
                    list($out, $index) = static::_generate($arr, $i+1);
                    $output[] = $out;
                    $i = $index;
                    break;
                case 'T_CLOSE':
                    return array($output, $i);
                    break;
                case 'T_ITEM':
                case 'T_NUMBER':
                    $output[] = $element;
                    break;
                case 'T_EMPTY':
                    $output[] = "";
                    break;
            }
        }
        return $output;
    }    
}

$input  = '[[["Hello, \"how\" are you?","Good!",,,123]],,"ok"]';
$items = Lexer::run($input);
print_r($items);

?>


然后请考虑给答案点赞/接受答案(左侧的绿色勾)。 - Jan

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