【问题标题】:Parsing a string to an array output from command line从命令行将字符串解析为数组输出
【发布时间】:2015-09-06 22:16:13
【问题描述】:

我正在开发一个新的 Symfony 2 项目,该项目将成为 Docker 容器的面板管理。

在这个项目中,我正在使用exec() PHP 函数执行一些命令。

我正在尝试解析以下命令的输出:

docker create tutum/lamp:latest --name test 2>&1

当命令成功时,我会在一个很好且易于使用的字符串中获取容器 ID,但是当出现问题时它就不一样了。结果是一个带有 var="data" 语法的字符串,我想解析它以获得一个数组。

命令输出:

time="2015-06-21T11:33:26+02:00" level="fatal" msg="Error response from daemon: Conflict. The name \"test\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name."

我希望有这样的东西:

Array( time => "2015-06-21T11:33:26+02:00", level => "fatal" ...);

我知道我必须进行正则表达式解析。过了一会儿(正则表达式和我并不是真正的好朋友)我得到了这个正则表达式(在https://regex101.com/ 测试):

/([a-zA-Z]+)="((.*)*)"/

我使用了 preg_split 函数,我不确定它是否好用。

preg_split('/([a-zA-Z]+)="((.*)*)"/', $output)

结果是:

array(2) { [0]=> string(0) "" [1]=> string(0) "" }

你有什么建议可以帮助我吗? 非常感谢您的帮助。

【问题讨论】:

  • 如果你只是 var_dump($output) 会发生什么?

标签: php regex bash symfony docker


【解决方案1】:

这是因为 greedy dot 将你的字符串吃掉到最后一个 "。让它变得懒惰,会这样做:

if(preg_match_all('/(\w+)="(.*?)(?<!\\\)"/s', $str, $out))
  print_r(array_combine($out[1], $out[2]));

\wshort[a-zA-Z0-9_]。向后看(?&lt;!\\\) 吃掉转义的引号 (see regex101)。

使用s 标志使点匹配换行符。 Test at eval.in输出到:

数组 ( [时间] => 2015-06-21T11:33:26+02:00 [级别] => 致命的 [msg] => 来自守护程序的错误响应:冲突。名称 \"test\" 已被容器 XXXXXXXX 使用。您必须删除(或重命名)该容器才能重用该名称。 )

【讨论】:

    【解决方案2】:

    TL;DR:这应该可行:

    preg_match_all(',([a-z]+)="((?:[^"]|\\\\")*[^\\\\])",', $a, $matches, PREG_SET_ORDER);
    var_dump($matches);
    

    最后一个var_dump打印如下数据结构,应该很容易处理:

    array(3) {
      [0] => array(3) {
        [0] => string(32) "time="2015-06-21T11:33:26+02:00""
        [1] => string(4) "time"
        [2] => string(25) "2015-06-21T11:33:26+02:00"
      }
      [1] => array(3) {
        [0] => string(13) "level="fatal""
        [1] => string(5) "level"
        [2] => string(5) "fatal"
      }
      [2] => array(3) {
        [0] => string(179) "msg="Error response from daemon: Conflict. The name \\"test\\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name.""
        [1] => string(3) "msg"
        [2] => string(173) "Error response from daemon: Conflict. The name \\"test\\" is already in use by container XXXXXXXX. You have to delete (or rename) that container to be able to reuse that name."
      }
    }
    

    为什么会这样

    正则表达式解释:

    ([a-z]+)                    # Match the label ("time", "level" or "msg")
    =                           # Self-explanatory
    "((?:[^"]|\\\\")*[^\\\\])"  # This is the tricky part:
                                # Match the quoted string; this is a sequence
                                # of (a) non-quote characters ([^"]) or
                                # (b) escaped quote characters (\\\\").
    

    其他一些注意事项:

    1. preg_split 使用正则表达式来匹配应该拆分字符串的标记。在这种情况下,这不是您想要的;您想返回正则表达式匹配的字符串部分。为此,您应该使用preg_match(或者,如果您希望一个模式匹配多次),preg_match_all
    2. 还要考虑PREG_SET_ORDER 标志preg_match_all。此标志使$matches 结果包含来自输​​出消息的每个标签的一行,这使得数据结构易于处理。试试看,如果你忽略它会发生什么。

    【讨论】:

    • 你太棒了,感谢你的帮助。如果我理解得很好: \\\\" 仅用于解析 \" 对吗?所以,例如,如果我想逃避 \n 我必须把 \\\\n ?
    • 乐于助人;是的,否则模式将在第一个引号处停止匹配(转义与否)。
    猜你喜欢
    • 2021-11-03
    • 2016-09-25
    • 2019-09-04
    • 2023-03-21
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    相关资源
    最近更新 更多