【问题标题】:Get more backreferences from regexp than parenthesis从正则表达式获得比括号更多的反向引用
【发布时间】:2010-02-11 15:09:58
【问题描述】:

好的,这个用英文很难解释,所以我就举个例子吧。

我将有以下格式的字符串:

key-value;key1-value;key2-...

我需要将数据提取为一个数组

array('key'=>'value','key1'=>'value1', ... )

我打算使用正则表达式来实现(大部分)这个功能,并写了这个正则表达式:

/^(\w+)-([^-;]+)(?:;(\w+)-([^-;]+))*;?$/

使用preg_match 和此代码:

for ($l = count($matches),$i = 1;$i<$l;$i+=2) {
    $parameters[$matches[$i]] = $matches[$i+1];
}

然而,正则表达式显然只返回 4 个反向引用——输入字符串的第一个和最后一个键值对。有没有解决的办法?我知道我可以使用正则表达式来测试字符串的正确性,并在循环中使用 PHP 的 explode 并获得完美的结果,但我真的很好奇是否可以使用正则表达式。

简而言之,我需要通过正则表达式在一个字符串中捕获任意数量的key-value; 对。

【问题讨论】:

    标签: php regex preg-match


    【解决方案1】:

    您可以在提取匹配项时使用前瞻来验证输入:

    /\G(?=(?:\w++-[^;-]++;?)++$)(\w++)-([^;-]++);?/
    

    (?=(?:\w++-[^;-]++;?)++$) 是验证部分。如果输入无效,匹配将立即失败,但每次应用正则表达式时仍会评估前瞻。为了保持它(以及正则表达式的其余部分)与键值对同步,我使用\G 将每个匹配锚定到上一个匹配结束的位置。

    这样,如果先行第一次成功,则保证以后每次都成功。显然它的效率并不高,但这可能不会成为问题——只有您的测试才能确定。

    如果前瞻失败,preg_match_all() 将返回零 (false)。如果成功,匹配项将以数组数组的形式返回:一个用于完整的键值对,一个用于键,一个用于值。

    【讨论】:

      【解决方案2】:

      正则表达式是强大的工具,但有时它并不是最好的方法。

      $string = "key-value;key1-value";
      $s = explode(";",$string);
      foreach($s as $k){
          $e = explode("-",$k);
          $array[$e[0]]=$e[1];
      }
      print_r($array);
      

      【讨论】:

      • 谢谢,但正如我在 OP 中所说:I know I can use regex just to test the correctness of the string and use PHP's explode in loops with perfect results
      • 所以...你知道这是可行的,但你不想使用它,因为你宁愿使用正则表达式,即使正则表达式不是适合这项工作的工具。正则表达式是一个真正的要求,还是你只是想这样做,因为它应该可以工作?
      • 阅读 OP,还是我应该再引用一遍?篇幅只有几段,如果你读起来太难,请不要使用自作聪明。
      【解决方案3】:

      请改用preg_match_all()。可能是这样的:

      $matches = $parameters = array();
      $input = 'key-value;key1-value1;key2-value2;key123-value123;';
      
      preg_match_all("/(\w+)-([^-;]+)/", $input, $matches, PREG_SET_ORDER);
      
      foreach ($matches as $match) {
         $parameters[$match[1]] = $match[2];
      }
      
      print_r($parameters);
      

      编辑:

      首先验证输入字符串是否符合模式,然后只需使用:

      if (preg_match("/^((\w+)-([^-;]+);)+$/", $input) > 0) {
          /* do the preg_match_all stuff */
      }       
      

      EDIT2:最后的分号是可选的

      if (preg_match("/^(\w+-[^-;]+;)*\w+-[^-;]+$/", $input) > 0) {
          /* do the preg_match_all stuff */
      }       
      

      【讨论】:

      • 因为这是我的问题的唯一答案,所以我给你一个+,如果没有人提出更好的解决方案,我会接受它,但正则表达式不会验证给定的字符串 (@987654327 @ 将被视为有效值)
      • 所以对于 'foo-bar-baz' 你想要 'foo' =&gt; 'bar-baz''foo-bar' =&gt; 'baz' 吗?我可以轻松地为您提供备用正则表达式;)
      • 对于'foo' =&gt; 'bar-baz',请改用正则表达式/(\w+)-([^;]+)/
      • 不,我宁愿它完全失败并且不给出任何结果,据我所知,唯一这样的正则表达式是我的(即使它不能用于获取数据,只能验证它)。很抱歉,我没有在我的 OP 中提到输入验证,但主要答案似乎很清楚。不过,我要等一天才能接受你的回答。
      • 再一次,前导分号 ; 是(高度)可选的,所以我的正则表达式仍然存在:)
      【解决方案4】:

      没有。较新的匹配会覆盖较旧的匹配。也许explode()limit 参数在爆炸时会有所帮助。

      【讨论】:

        【解决方案5】:

        这个解决方案怎么样:

        $samples = array(
            "good" => "key-value;key1-value;key2-value;key5-value;key-value;",
            "bad1" => "key-value-value;key1-value;key2-value;key5-value;key-value;",
            "bad2" => "key;key1-value;key2-value;key5-value;key-value;",
            "bad3" => "k%ey;key1-value;key2-value;key5-value;key-value;"
        );
        
        foreach($samples as $name => $value) {
            if (preg_match("/^(\w+-\w+;)+$/", $value)) {
                printf("'%s' matches\n", $name);
            } else {
                printf("'%s' not matches\n", $name);
            }
        }
        

        【讨论】:

        • 不需要最后的分号;。此外,这仅验证输入,我希望正则表达式验证并创建一个数组。
        • 是的,我还没有意识到你也需要数据。
        【解决方案6】:

        我认为您不能使用一个正则表达式同时进行数据验证和提取,因为您需要锚点(^$)进行验证,preg_match_all() 用于数据,但如果您使用锚点使用preg_match_all() 只会返回最后匹配的集合。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-01
          相关资源
          最近更新 更多