PHP字符串控制台参数到数组

Cri*_*tos 17 php regex arrays explode preg-match

我想知道如何将给定的字符串转换为指定的数组:

all ("hi there \(option\)", (this, that), other) another
Run Code Online (Sandbox Code Playgroud)

想要的结果(数组)

[0] => all,
[1] => Array(
    [0] => "hi there \(option\)",
    [1] => Array(
        [0] => this,
        [1] => that
    ),
    [2] => other
),
[2] => another
Run Code Online (Sandbox Code Playgroud)

这用于我在PHP上制作的一种控制台.我尝试使用preg_match_all但是,我不知道如何在括号内找到括号以"在数组中创建数组".

编辑

应将示例中未指定的所有其他字符视为String.

编辑2

我忘了提到括号外的所有参数都应该被space字符检测到.

Jon*_*Jon 14

10,000英尺的概述

您需要使用一个小的自定义解析器执行此操作:代码接受此表单的输入并将其转换为您想要的表单.

在实践中,我发现基于它们的复杂性将这样的解析问题分组在三个类别之一中是有用的:

  1. 琐碎:可以通过一些循环和人性正则表达式解决的问题.这个类别是诱人的:如果你甚至不确定问题是否可以通过这种方式解决,一个好的经验法则是决定它不能.
  2. 简单:需要自己构建一个小解析器的问题,但仍然很简单,以至于带出大枪是没有意义的.如果您需要编写超过100行代码,请考虑升级到下一个类别.
  3. 参与:正式使用已经存在的,经过验证的解析器生成器¹有意义的问题¹.

我将此特定问题归类为属于第二类,这意味着您可以像这样处理它:

写一个小解析器

定义语法

要做到这一点,你必须首先定义-至少是非正式,用几个简单的音符-在语法要解析.请记住,大多数语法都是在某些时候递归定义的.所以我们说我们的语法是:

  • 输入是一个序列
  • 序列是一系列一系列零个或多个令牌
  • 令牌可以是单词,字符串数组
  • 标记由一个或多个空白字符分隔
  • 一个单词是一系列字母字符(az)
  • 是双引号中的任意字符序列
  • 一个阵列是一系列的一个或多个令牌被分离逗号

你可以看到我们在一个地方有递归:一个序列可以包含数组,一个数组也是按照一个序列定义的(所以它可以包含更多的数组等).

如上所述,非正式地处理这个问题比作为介绍更容易,但如果你正式地做,那么对语法的推理会更容易.

建立一个词法分析器

掌握语法后,您需要将输入分解为令牌,以便可以对其进行处理.将用户输入并将其转换为由语法定义的单个片段的组件称为词法分析器.词霸是愚蠢的; 他们只关心输入的"外观",不要试图检查它是否真的有意义.

这是我写的一个简单的词法分析器来解析上面的语法(不要将它用于任何重要的东西;可能包含错误):

$input = 'all ("hi there", (this, that) , other) another';

$tokens = array();
$input = trim($input);
while($input) {
    switch (substr($input, 0, 1)) {
        case '"':
            if (!preg_match('/^"([^"]*)"(.*)$/', $input, $matches)) {
                die; // TODO: error: unterminated string
            }

            $tokens[] = array('string', $matches[1]);
            $input = $matches[2];
            break;
        case '(':
            $tokens[] = array('open', null);
            $input = substr($input, 1);
            break;
        case ')':
            $tokens[] = array('close', null);
            $input = substr($input, 1);
            break;
        case ',':
            $tokens[] = array('comma', null);
            $input = substr($input, 1);
            break;
        default:
            list($word, $input) = array_pad(
                preg_split('/(?=[^a-zA-Z])/', $input, 2),
                2,
                null);
            $tokens[] = array('word', $word);
            break;
    }
    $input = trim($input);
}

print_r($tokens);
Run Code Online (Sandbox Code Playgroud)

构建解析器

完成此操作后,下一步是构建一个解析器:一个检查lexed输入并将其转换为所需格式的组件.解析器很聪明; 在转换输入的过程中,它还确保输入由语法规则很好地形成.

解析器通常实现为状态机(也称为有限状态机或有限自动机),并且工作方式如下:

  • 解析器有一个状态 ; 这通常是适当范围内的数字,但每个州也用更友好的名称来描述.
  • 有一个循环读取一次读取一个lexed令牌.根据当前状态和令牌的值,解析器可能决定执行以下一项或多项操作:
    1. 采取一些影响其输出的行动
    2. 将其状态更改为其他值
    3. 确定输入结果不正确并产生错误

¹解析器生成器是一些程序,其输入是一个形式语法,其输出是词法分析器和解析器,你可以"只是加水":只需扩展代码,根据令牌的类型执行"采取一些行动"; 其他一切都已经得到了解决.快速搜索这个主题给出了PHP Lexer和Parser Generator?

  • @Dyin:我也不是正则表达式的专家,但我知道我永远不会想要用正则表达式来做这个因为a)我认为不可能*证明正则表达式在所有情况下都能正常工作(虽然肯定有可能证明解析器正确处理给定的正式语法)和b)更容易*推理*关于FSM如何工作,因此扩展和维护更容易.因人而异. (3认同)
  • @Dyin:这取决于你如何定义"需要".如果你想要一个可维护的解析器,那么你绝对需要**语法.如果你想要一个适合我的正则表达式(tm)但是完全不可理解,可能会受到最轻微的挑衅而最终无法延伸,那么你不一定需要**语法.如果你不同意,请尝试通过编写这样的正则表达式来证明我的错误. (2认同)

Ler*_*eri 4

如果您正在构建语法树,那么毫无疑问您应该编写解析器。但如果您只需要解析示例输入,regex仍然可能是一个工具:

<?php
$str = 'all, ("hi there", (these, that) , other), another';

$str = preg_replace('/\, /', ',', $str); //get rid off extra spaces
/*
 * get rid off undefined constants with surrounding them with quotes
*/
$str = preg_replace('/(\w+),/', '\'$1\',', $str);
$str = preg_replace('/(\w+)\)/', '\'$1\')', $str);
$str = preg_replace('/,(\w+)/', ',\'$1\'', $str);

$str = str_replace('(', 'array(', $str);

$str = 'array('.$str.');';

echo '<pre>';
eval('$res = '.$str); //eval is evil.
print_r($res); //print the result
Run Code Online (Sandbox Code Playgroud)

演示

注意:如果输入格式错误,正则表达式肯定会失败。我编写这个解决方案只是为了防止您需要快速脚本。编写词法分析器和解析器是一项耗时的工作,需要大量的研究。