【问题标题】:string to array, split by single and double quotes字符串到数组,用单引号和双引号分隔
【发布时间】:2012-09-03 10:15:50
【问题描述】:

我正在尝试使用 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))/";

但显然这是行不通的。

【问题讨论】:

    标签: php preg-split


    【解决方案1】:

    您可以将preg_split 与PREG_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);
    

    【讨论】:

    • 我相信您的解决方案更优雅。 +1
    • 这就是我所追求的。令人惊讶的是它不需要反向引用,但很棒!
    • 如果我想扩展正则表达式以忽略转义的引号会很容易吗?例如$str = "the 'cat s\'at on' the mat" 应该给[0] =&gt; the , [1] =&gt; 'cat s\'at on', [2] =&gt; the mat。如果没有,那么我将为此添加一个新的新问题。干杯!
    • "/('(?:.*)(?&lt;!\\\\)(?&gt;\\\\\\\\)*'|\"(?:.*)(?&lt;!\\\\)(?&gt;\\\\\\\\)*\")/U" - 是的,所有这些反斜杠都是必需的。
    • 这将允许反斜杠转义引号。双反斜杠不会转义引号,而是文字反斜杠。拆分后您必须删除额外的内容。
    【解决方案2】:

    您可以只使用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 中捕获直到结束

    【讨论】:

      【解决方案3】:

      另一个使用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
      

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

      【讨论】:

        【解决方案4】:

        您可以在preg_match_all 中使用back references 和ungreedy modifier

        $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'
        

        您可以使用substr 和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
        

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

        【讨论】:

        • 确实有效,但它不会返回匹配之外的位,这是我需要的
        • 这是一个可能的解决方案。如果没有人想出一个单一的正则表达式方法,那么我将奖励答案。但我更喜欢单个正则表达式,因为它会更简单。
        • 我更新了我的解决方案以包含匹配之外的位,可以吗?
        • 它做了我所追求的,但我仍然更喜欢单行正则表达式。无论如何,感谢这个解决方案 - 如果没有人发布单行解决方案,那么我将奖励这个:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-30
        • 2015-08-05
        • 2019-11-09
        相关资源
        最近更新 更多