使用经典的 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