PHP自定义命令解析器

时间:2020-08-14 03:26:57

标签: php regex parsing

我是一个电报机器人,很难从字符串中获取输入值。我的目标是使我的命令易于编写和理解。这是我从客户消息中获得的一些示例命令

,simpleImage. query Dark background. width 200. height 200. text %fortune_teller%

应该是这样的数组

[simple_image][query] => 'Dark background'
[simple_image][width] => '200'
[simple_image][height] => '200'
[simple_image][text] => '%fortune_teller%'

对于空参数,应该这样

,sayHello. %custom_id%
[say_hello] => '%custom_id%'

但是当我尝试输入一些日语文本和诸如\, *, &, |, et cetera之类的特殊字符时,我遇到了一些问题

,cmd. cat files.txt 2>/dev/null && find $HOME -type f -iname \*.mp3 -delete

这是我到目前为止编写的一些代码

<?php

function parse($string) {
    preg_match_all("/,?([a-z_]+)?\..([a-z]+)\s([a-z\s%_]+)/", $string, $tmp);
    $query = array_combine($tmp[2], $tmp[3]);

    if(!empty($tmp[1])) {
        return array_merge([$tmp[1][0]=>true], $query);
    }
}
var_dump(parse($update['message']));

1 个答案:

答案 0 :(得分:0)

我几乎从不建议使用命名捕获组,因为它们只会使模式和匹配项的输出数组膨胀,但是对于那些喜欢它们的人,您可以使用以下方法:

~(?:^,(?<COMMANDS>[^.]+)|\G(?!^))\.(?= )(?: (?<KEYS>\S+))? (?<VALUES>.+?(?=\. |$))~

否则:

~~(?:^,([^.]+)|\G(?!^))\.(?= )(?: (\S+))? (.+?(?=\. |$))~~

Pattern Demo

有效地,您应该使用\G(继续元字符)继续匹配前导命令子字符串后可变数量的序列。我的模式基于以下事实:命令和后续键值对由点后紧跟一个空格分隔。如果您不能100%依靠此定界顺序,则需要解决此问题,然后再尝试前进。

一旦有了matchs数组(Demo),您只需要对其进行迭代以构建所需的输出结构即可。

代码:(Demo

$commands = [
    ',simpleImage. query Dark background. width 200. height 200. text %fortune_teller%',
    ',sayHello. %custom_id%',
    ',cmd. cat files.txt 2>/dev/null && find $HOME -type f -iname \*.mp3 -delete',
];

foreach ($commands as $command) {
    $result = [];
    if (preg_match_all('~(?:^,([^.]+)|\G(?!^))\.(?= )(?: (\S+))? (.+?(?=\. |$))~', $command, $out)) {
        foreach ($out[2] as $index => $subKey) {
            if (strlen($subKey)) {
                $result[$out[1][0]][$subKey] = $out[3][$index];
            } else {
                $result[$out[1][0]] = $out[3][$index];
            }
        }
    }
    echo var_export($result, true) . "\n---\n";
}

输出:

array (
  'simpleImage' => 
  array (
    'query' => 'Dark background',
    'width' => '200',
    'height' => '200',
    'text' => '%fortune_teller%',
  ),
)
---
array (
  'sayHello' => '%custom_id%',
)
---
array (
  'cmd' => 
  array (
    'cat' => 'files.txt 2>/dev/null && find $HOME -type f -iname \\*.mp3 -delete',
  ),
)
---