PHP:解析单引号、双引号和圆括号外以逗号分隔的字符串


PHP: Parse comma-delimited string outside single and double quotes and parentheses

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

我试图解析用户生成的字符串,就好像它是一系列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'"),")
}

任何正则表达式或解析器函数的帮助,以完成这是感激的!

使用传统的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);
演示

您可以在,$处拆分参数字符串,然后将$附加到数组值后面:

$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'"),")
)

如果你想使用正则表达式,你可以这样做:

(.+?)(?:,(?='$)|$)
演示工作

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'"),")`