繁体   English   中英

Java:如何用单个字符替换连续字符?

[英]Java: How to replace consecutive characters with a single character?

如何用 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);
}

当前 output: def!!mnop!UVW

所需的 output: def!mnop!UVW

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

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

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

为此使用正则表达式的最大困难是从您的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等),则会中断。

s = s.replaceAll("([ \\.])[ \\.]+", "$1");

或者如果只需要替换几个相同的分隔符:

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

虽然不使用正则表达式,但我认为需要使用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

解释:

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

.codePoints() - 将String拆分为Stream

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

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

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

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

.collect(Collectors.joining("")) - 最后我们可以将字符连接在一起以重构一个String

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM