PHP:在单引号和双引号以及括号之外解析逗号分隔的字符串

时间:2015-05-23 04:16:18

标签: php regex parsing

我找到了这个问题的几个部分答案,但没有一个能满足我的所有需求......

我正在尝试解析用户生成的字符串,就好像它是一系列php函数参数来确定参数的数量:

此字符串:

$arg1,$arg2='ABC,DEF',$arg3="GHI\",JKL",$arg4=array(1,'2)',"3\"),")

将作为函数的参数插入:

function my_function( [insert string here] ){ ... }

我需要解析逗号上的字符串,考虑单引号和双引号,括号以及转义的引号和括号来创建数组:

array(4) {
  [0] => $arg1
  [1] => $arg2='ABC,DEF'
  [2] => $arg3="GHI\",JKL"
  [3] => $arg4=array(1,'2)',"3\"),")
}

任何有关正则表达式或解析器函数的帮助都是值得赞赏的!

3 个答案:

答案 0 :(得分:1)

使用传统的csv工具无法解决此问题,因为有多个字符能够保护字符串的某些部分。 使用preg_split是可能的,但会导致非常复杂和低效的模式。所以最好的方法是使用preg_match_all。但是有几个问题需要解决:

  • 根据需要,用引号或括号括起来的逗号必须被忽略(看作没有特殊含义的字符,不作为分隔符)
  • 你需要提取参数,但你需要检查字符串是否也有良好的格式,否则匹配结果可能完全错误!

对于第一点,您可以定义子模式来描述每种情况:引用的部分,括号之间的部分,以及能够匹配完整参数的更一般的子模式,以及在需要时使用前两个子模式。

请注意,括号子模式也需要引用通用子模式,因为它可以包含任何内容(以及逗号)。

可以使用\G锚来解决第二点,确保所有匹配都是连续的。但是你需要确保已经达到了字符串的结尾。为此,您可以在主模式的末尾添加一个可选的空捕获组,仅当字符串\z的结尾的锚点成功时才会创建。

$subject = <<<'EOD'
$arg1,$arg2='ABC,DEF',$arg3="GHI\",JKL",$arg4=array(1,'2)',"3\"),")
EOD;

$pattern = <<<'EOD'
~
  # named groups definitions
  (?(DEFINE) # this definition group allows to define the subpatterns you want
             # without matching anything
      (?<quotes>
          ' [^'\\]*+ (?s:\\.[^'\\]*)*+ ' | " [^"\\]*+ (?s:\\.[^"\\]*)*+ "
      )
      (?<brackets> \( \g<content> (?: ,+ \g<content> )*+ \) )
      (?<content> [^,'"()]*+        # ' # (<-- comment for SO syntax highlighting)
                  (?:
                      (?: \g<brackets> | \g<quotes> )
                      [^,'"()]*     # ' #
                  )*+
      )
  )
  # the main pattern
  (?: # two possible beginings
      \G(?!\A) , # a comma contiguous to a previous match
    |            #  OR
      \A         # the start of the string
  ) 
  (?<param> \g<content> )
  (?: \z (?<check>) )? # create an item "check" when the end is reached
~x
EOD;

$result = false;

if ( preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER) &&
     isset(end($matches)['check']) )
    $result = array_map(function ($i) { return $i['param']; }, $matches);
else 
   echo 'bad format' . PHP_EOL;

var_dump($result);

demo

答案 1 :(得分:0)

您可以将参数字符串拆分为,$,然后将$追加回数组值:

$args_array = explode(',$', $arg_str);
foreach($args_array as $key => $arg_raw) {
    $args_array[$key] = '$'.ltrim($arg_raw, '$');
}
print_r($args_array);

输出:

(
    [0] => $arg1
    [1] => $arg2='ABC,DEF'
    [2] => $arg3="GHI\",JKL"
    [3] => $arg4=array(1,'2)',"3\"),")
)

答案 2 :(得分:0)

如果您想使用正则表达式,可以使用以下内容:

(.+?)(?:,(?=\$)|$)

<强> Working demo

Php代码:

$re = '/(.+?)(?:,(?=\$)|$)/'; 
$str = "\$arg1,\$arg2='ABC,DEF',\$arg3=\"GHI\",JKL\",\$arg4=array(1,'2)',\"3\"),\")\n"; 

preg_match_all($re, $str, $matches);

匹配信息:

MATCH 1
1.  [0-5]   `$arg1`
MATCH 2
1.  [6-21]  `$arg2='ABC,DEF'`
MATCH 3
1.  [22-39] `$arg3="GHI\",JKL"`
MATCH 4
1.  [40-67] `$arg4=array(1,'2)',"3\"),")`