【问题标题】:Remove repetitve Consecutive words from string从字符串中删除重复的连续单词
【发布时间】:2019-11-29 21:15:18
【问题描述】:

我想从字符串中删除重复的单词(只有连续的)。

$str = 'abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna';

我想要的输出字符串是:

abc,def,fgh,xna,fgh,xyz,tr,xna

我可以使用这个在 php 中得到我想要的结果:

$ip = explode(',', $str);
$op = [];$last = null;
for($i=0;$i<count($ip);$i++){
    if ($last == $ip[$i]) {
        continue;
    }
    $op[]=$last=$ip[$i];
}
$ip = implode(',', $op);

但正在寻找正则表达式方法。到目前为止,我已经更接近这两个正则表达式:

$after = preg_replace('/(?:^|,)([^,]+)(?=.*,\1(?:,|$))/m', '', $str);
output : abc,def,fgh,xyz,tr,xna

$after = preg_replace('/([^,]+)(,[ ]*\1)+/m', '', $str);
output : abc,degh,fgh,xna,fgh,,,xna

【问题讨论】:

    标签: php regex preg-replace


    【解决方案1】:

    你应该使用

    preg_replace('~(?<![^,])([^,]+)(?:,\1)+(?![^,])~', '$1', $str)
    

    regex demo

    如果需要在逗号和重复值之间支持任何 0 个或多个空格字符,add \s*0 个或多个空格)模式在 \1 之前。

    详情

    • (?&lt;![^,]) - 字符串或除逗号以外的任何字符的开头
    • ([^,]+) - 第 1 组:除逗号之外的任何一个或多个字符
    • (?:,\1)+ - 一个或多个逗号序列和第 1 组中的值
    • (?![^,]) - 字符串结尾或逗号以外的字符。

    【讨论】:

    • @Progrock 这个表达式不是自动生成的,要点是 1)你需要一个起始边界,2)捕获组内的重复模式,3)一个带有分隔符的组和对 Group 的反向引用1 应该重复 1 次或更多次,然后 4) 尾随边界。没有边界,第 1 组中捕获的值的一部分may get matched erroneously
    【解决方案2】:
    $after = preg_replace('/(?<=^|,)([^,]+)(,\s*\1)+/', '$1', $str);
    

    附:如果, 之后没有空格期望,您可以从上面的正则表达式中删除\s*。我刚刚查看了您的[ ]*,并认为您可能有空格。

    【讨论】:

    • (很抱歉从您的解决方案中获取和发布错误的输出,因为我之前未能正确复制此正则表达式。)
    【解决方案3】:

    用 strtok 迭代,只粘上不一样的部分:

    <?php
    
    $str = 'abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna';
    
    $out = $last = strtok($str, ',');
    while($current = strtok(','))
        if($current !== $last)
            $out .= ',' . ($last = $current);
    
    echo $out;
    

    输出:

    abc,def,fgh,xna,fgh,xyz,tr,xna
    

    【讨论】:

      【解决方案4】:

      array_reduce:

      $arr = explode(',', $str);
      $prev = array_shift($arr);
      $result = array_reduce($arr, function($c, $i) use (&$prev) {
          if ($prev==$i) return $c;
          $prev=$i;
          return "$c,$i";
      }, $prev);
      

      【讨论】:

        【解决方案5】:

        我会这样解决:

        $after = preg_replace('/(?<=,|^)([^,]+)\K(,\1)+(?=,|$)/', '', $str);
        

        这将输出abc,def,fgh,xna,fgh,xyz,tr,xna

        它的作用:

        • (?&lt;=,|^) 查看 是逗号还是前面开始的字符串
        • ([^,]+) 匹配除逗号以外的任何内容(搜索模式)
        • \K 重置内部光标并“忘记”之前的内容(例如,它不再算作匹配)
        • (,\1)+ 匹配第一个定义的搜索模式的多次出现
        • (?=,|$) 查看下一个字符是否再次为逗号或字符串是否结束

        所以我们的想法是,选择任何模式的重复(仅重复)并将它们替换为空。

        更新:

        通过添加(?=,|$) 修复了模式。否则此测试输入将失败 并彻底杀死 xna 部分。

        $str = 'bc,abc,abc,abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna,xna,xnabc';
        

        在这里测试:https://regex101.com/r/Yv1htV/3

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-03-14
          • 2018-08-05
          • 2015-01-26
          • 2013-08-11
          • 2017-04-03
          相关资源
          最近更新 更多