[英]Replace multiple consecutive occurrences of a character with a single occurrence
[英]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());
}
}
为此使用正则表达式的最大困难是从您的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()
行为(在空字符串的拆分中产生前导空格),不确定这是如何向后兼容,但对于当前版本,这应该没问题。
编辑:事实上,如果分隔字符包含代表未转义的正则表达式标记的数字或字符(即1
、 b
等),则会中断。
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.