【问题标题】:Java: How to replace consecutive characters with a single character?Java:如何用单个字符替换连续字符?
【发布时间】:2020-07-19 05:53:44
【问题描述】:

如何在java中用单个字符替换连续字符?

String fileContent = "def  mnop.UVW";
String oldDelimiters = " .";
String newDelimiter = "!";
for (int i = 0; i < oldDelimiters.length(); i++){
    Character character = oldDelimiters.charAt(i);
    fileContent = fileContent.replace(String.valueOf(character), newDelimiter);
}

当前输出:def!!mnop!UVW

所需输出:def!mnop!UVW

请注意,这两个空格被替换为两个感叹号。如何用一个分隔符替换连续的分隔符?

【问题讨论】:

  • oldDelimiters 有两个字符。
  • @SudhirOjha 没错。这就是我遍历 oldDelimiters 的原因。
  • 尝试使用一对多量化字符类的正则表达式;例如:String result = text.replaceAll("[ .]+", "!");.
  • @ChrisSmith 取决于您的 oldDelimiter 字符串;如果它包含一个字符(或几个单个字符),则使用 "[" + oldDelimiter + "]+" 之类的东西作为表达式部分;如果是单个字符串,请使用 "(?:" + oldDelimiter + ")+" 之类的内容。很抱歉进行了修改。
  • @ChrisSmith 我完全赞成用算法解决问题,但在某些情况下,RE 非常方便快速解决问题。

标签: java regex string algorithm


【解决方案1】:

由于您想匹配旧分隔符中的连续字符,因此正则表达式解决方案在这里似乎不可行。如果它属于旧的定界符之一,您可以逐个字符地匹配它,然后将其设置为新的,如下所示。

import java.util.*;
public class Main{
    public static void main(String[] args) {
        String fileContent = "def  mnop.UVW";
        String oldDelimiters = " .";

        // add all old delimiters in a set for fast checks
        Set<Character> set = new HashSet<>();
        for(int i=0;i<oldDelimiters.length();++i) set.add(oldDelimiters.charAt(i));

        /* 
           match all consecutive chars at once, check if it belongs to an old delimiter 
           and replace it with the new one
        */

        String newDelimiter = "!";
        StringBuilder res = new StringBuilder("");
        for(int i=0;i<fileContent.length();++i){
            if(set.contains(fileContent.charAt(i))){
                while(i + 1 < fileContent.length() && fileContent.charAt(i) == fileContent.charAt(i+1)) i++;
                res.append(newDelimiter);
            }else{
                res.append(fileContent.charAt(i));        
            }
        }

        System.out.println(res.toString());
    }
}

演示: https://onlinegdb.com/r1BC6qKP8

【讨论】:

    【解决方案2】:
    s = s.replaceAll("([ \\.])[ \\.]+", "$1");
    

    或者如果只有几个相同的分隔符必须被替换:

    s = s.replaceAll("([ \\.])\\1+", "$1");
    
    • [....] 是一组替代字符
    • 第一个 (...) 是第 1 组,$1
    • \\1是第一组的文字

    【讨论】:

      【解决方案3】:

      虽然不使用正则表达式,但我认为需要使用 StreamS 的解决方案,因为每个人都喜欢流:

      private static class StatefulFilter implements Predicate<String> {
          private final String needle;
          private String last = null;
      
          public StatefulFilter(String needle) {
              this.needle = needle;
          }
      
          @Override
          public boolean test(String value) {
              boolean duplicate = last != null && last.equals(value) && value.equals(needle);
              last = value;
              return !duplicate;
          }
      }
      
      public static void main(String[] args) {
          System.out.println(
              "def  mnop.UVW"
              .codePoints()
              .sequential()
              .mapToObj(c -> String.valueOf((char) c))
              .filter(new StatefulFilter(" "))
              .map(x -> x.equals(" ") ? "!" : x)
              .collect(Collectors.joining(""))
          );
      }
      

      可运行示例:https://onlinegdb.com/BkY0R2twU

      说明:

      理论上,你不应该真的有一个有状态的过滤器,但从技术上讲,只要流没有被并行化,它就可以正常工作:

      .codePoints() - 将String 拆分为Stream

      .sequential() - 因为我们关心字符的顺序,所以我们的Stream 可能不会被并行处理

      .mapToObj(c -&gt; String.valueOf((char) c)) - 如果我们转换为String,过滤器中的比较会更直观,但实际上并不需要

      .filter(new StatefulFilter(" ")) - 在这里我们过滤掉在另一个空格之后的任何空格

      .map(x -&gt; x.equals(" ") ? "!" : x) - 现在我们可以用感叹号替换剩余的空格

      .collect(Collectors.joining("")) - 最后我们可以将角色连接在一起重新构成String

      StatefulFilter 本身非常简单 - 它检查 a) 我们是否有前一个字符,b) 前一个字符是否与当前字符相同,以及 c) 当前字符是否是分隔符 (空间)。仅当所有 a、b 和 c 都为真时,它才会返回 false(意味着字符被删除)。

      【讨论】:

        【解决方案4】:

        为此使用正则表达式的最大困难是从您的oldDelimiters 字符串创建一个表达式。例如:

        String oldDelimiters = " .";
        String expression = "\\" + String.join("+|\\", oldDelimiters.split("")) + "+";
        String text = "def  mnop.UVW;abc .df";
        String result = text.replaceAll(expression, "!");
        

        (编辑:由于表达式中的字符现在无论如何都被转义了,我删除了字符类并编辑了以下文本以反映该更改。)

        生成的表达式看起来像\ +|\.+,即每个字符都被量化并构成表达式的一个替代项。如果可以匹配,引擎将一次匹配并替换一个替代方案。 result 现在包含:

        def!mnop!UVW;abc!!df
        

        由于以前版本的 Java 中的 split() 行为(在拆分空字符串时产生前导空格),不确定这是如何向后兼容,但对于当前版本,这应该没问题。

        编辑:事实上,如果分隔字符包含代表未转义的正则表达式标记的数字或字符(即1b 等),则会中断。

        【讨论】:

        • 转义字符可能会更好。这样,即使包含 '^'、'\' 或 ']' 的 oldDelimiters 也可以使用
        • @Taemyr 这很公平,如果我知道在 Java 中实现这一点的直接方法,我会包含它。由于这些字符不是问题的一部分,所以我在演示中省略了。
        • @Taemyr Omg,我只见树木不见森林。我在考虑模式引用函数,但我可以添加反斜杠。 ^^;请参阅我的编辑。感谢您的询问。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-29
        • 2010-10-23
        • 2019-12-26
        • 1970-01-01
        相关资源
        最近更新 更多