3

我正在尝试使用 php 将字符串拆分为使用"'作为分隔符的数组组件。我只想按最外面的字符串拆分。以下是四个示例以及每个示例的预期结果:

$pattern = "?????";
$str = "the cat 'sat on' the mat";
$res = preg_split($pattern, $str);
print_r($res);
/*output:
Array
(
    [0] => the cat 
    [1] => 'sat on'
    [2] =>  the mat
)*/

$str = "the cat \"sat on\" the mat";
$res = preg_split($pattern, $str);
print_r($res);
/*output:
Array
(
    [0] => the cat 
    [1] => "sat on"
    [2] =>  the mat
)*/

$str = "the \"cat 'sat' on\" the mat";
$res = preg_split($pattern, $str);
print_r($res);
/*output:
Array
(
    [0] => the
    [1] => "cat 'sat' on"
    [2] =>  the mat
)*/

$str = "the 'cat \"sat\" on' the mat 'when \"it\" was' seventeen";
$res = preg_split($pattern, $str);
print_r($res);
/*output:
Array
(
    [0] => the
    [1] => 'cat "sat" on'
    [2] =>  the mat
    [3] => 'when "it" was'
    [4] =>  seventeen
)*/

如您所见,我只想按最外层的引号分割,并且我想忽略引号中的任何引号。

我想出的最接近的$pattern

$pattern = "/((?P<quot>['\"])[^(?P=quot)]*?(?P=quot))/";

但显然这是行不通的。

4

4 回答 4

2

您可以使用preg_splitPREG_SPLIT_DELIM_CAPTURE选项。正则表达式不如@Jan Turoň 的反向引用方法那么优雅,因为所需的捕获组会弄乱结果。

$str = "the 'cat \"sat\" on' the mat the \"cat 'sat' on\" the mat";
$match = preg_split("/('[^']*'|\"[^\"]*\")/U", $str, null, PREG_SPLIT_DELIM_CAPTURE);
print_r($match);
于 2012-09-10T15:26:08.857 回答
1

您可以仅用preg_match于此目的:

$str = "the \"cat 'sat' on\" the mat";
$pattern = '/^([^\'"]*)(([\'"]).*\3)(.*)$/';

if (preg_match($pattern, $str, $matches)) {
  printf("[initial] => %s\n[quoted] => %s\n[end] => %s\n",
     $matches[1],
     $matches[2],
     $matches[4]
  );
}

这打印:

[initial] => the 
[quoted] => "cat 'sat' on"
[end] =>  the mat

这是正则表达式的解释:

  • /^([^\'"]*)=> 将初始位放在第一个捕获组中的第一个引号(单引号或双引号)之前
  • (([\'"]).*\3)=> 在 \2 中捕获从初始引号(单引号或双引号)(在 \3 中捕获)到结束引号(必须与开始引号的类型相同,因此是 \3)对应的文本。正则表达式本质上是贪婪的这一事实有助于从第一个引号到最后一个引号,无论里面有多少引号。
  • (.*)$/=> 在 \4 中捕获直到结束
于 2012-09-10T15:27:39.000 回答
1

另一个使用preg_replace_callback的解决方案

$result1 = array();
function parser($p) {
  global $result1;
  $result1[] = $p[0];
  return "|"; // temporary delimiter
}

$str = "the 'cat \"sat\" on' the mat 'when \"it\" was' seventeen";
$str = preg_replace_callback("/(['\"]).*\\1/U", "parser", $str);
$result2 = explode("|",$str); // using temporary delimiter

现在您可以使用压缩这些数组array_map

$result = array();
function zipper($a,$b) {
  global $result;
  if($a) $result[] = $a;
  if($b) $result[] = $b;
}
array_map("zipper",$result2,$result1);
print_r($result);

结果是

[0] => the 
[1] => 'cat "sat" on'
[2] =>  the mat 
[3] => 'when "it" was'
[4] =>  seventeen

注意:我可能会更好地创建一个执行此壮举的类,因此可以避免使用全局变量。

于 2012-09-10T15:33:14.613 回答
0

您可以在中使用反向引用不贪婪的修饰符preg_match_all

$str = "the 'cat \"sat\" on' the mat 'when \"it\" was' seventeen";
preg_match_all("/(['\"])(.*)\\1/U", $str, $match);
print_r($match[0]);

现在你有了最外层的报价部分

[0] => 'cat "sat" on'
[1] => 'when "it" was'

你可以用substrand找到字符串的其余部分strpos(一种黑盒解决方案)

$a = $b = 0; $result = array();
foreach($match[0] as $part) {
  $b = strpos($str,$part);
  $result[] = substr($str,$a,$b-$a);
  $result[] = $part;
  $a = $b+strlen($part);
}
$result[] = substr($str,$a);
print_r($result);

这是结果

[0] => the 
[1] => 'cat "sat" on'
[2] =>  the mat 
[3] => 'when "it" was'
[4] =>  seventeen

如果引号位于字符串的开头/结尾,则只需去除最终的空标题/尾随元素。

于 2012-09-10T14:39:20.537 回答