Java正则表达式:除了给定字符串的实例外,用`+`替换所有字符

fsd*_*dff 23 java regex

我有以下问题说明

使用+符号替换字符串中的所有字符,但方法中给定字符串的实例除外

所以例如,如果给出的字符串是,abc123efg并且他们希望我替换除了每个实例之外的每个字符,123那么它将成为+++123+++.

我认为正则表达式可能是最好的,我想出了这个.

str.replaceAll("[^str]","+") 
Run Code Online (Sandbox Code Playgroud)

其中str是一个变量,但它不允许我使用该方法而不将其放在引号中.如果我只想替换变量字符串str我该怎么做?我用字符串手动输入它运行它,它在方法上工作,但我可以输入一个变量吗?

截至目前我相信它正在寻找字符串"str"而不是变量字符串.

这是除了两个以外的很多情况的输出权:(

在此输入图像描述

开放测试用例列表:

plusOut("12xy34", "xy") ? "++xy++"
plusOut("12xy34", "1") ? "1+++++"
plusOut("12xy34xyabcxy", "xy") ? "++xy++xy+++xy"
plusOut("abXYabcXYZ", "ab") ? "ab++ab++++"
plusOut("abXYabcXYZ", "abc") ? "++++abc+++"
plusOut("abXYabcXYZ", "XY") ? "++XY+++XY+"
plusOut("abXYxyzXYZ", "XYZ") ? "+++++++XYZ"
plusOut("--++ab", "++") ? "++++++"
plusOut("aaxxxxbb", "xx") ? "++xxxx++"
plusOut("123123", "3") ? "++3++3"
Run Code Online (Sandbox Code Playgroud)

nha*_*tdh 15

看起来这是plusOutCodingBat上的问题.

我有3个解决这个问题的方法,并为了好玩而写了一个新的流媒体解决方案.

解决方案1:循环和检查

从输入字符串中创建一个StringBuilder,并检查每个位置的单词.如果不匹配则替换字符,如果找到则跳过单词的长度.

public String plusOut(String str, String word) {
  StringBuilder out = new StringBuilder(str);

  for (int i = 0; i < out.length(); ) {
    if (!str.startsWith(word, i))
      out.setCharAt(i++, '+');
    else
      i += word.length();
  }

  return out.toString();
}
Run Code Online (Sandbox Code Playgroud)

这可能是初学者程序员的预期答案,尽管假设字符串不包含任何星体平面字符,它将由2个字符而不是1表示.

解决方案2:用标记替换单词,替换其余单词,然后恢复单词

public String plusOut(String str, String word) {
    return str.replaceAll(java.util.regex.Pattern.quote(word), "@").replaceAll("[^@]", "+").replaceAll("@", word);
}
Run Code Online (Sandbox Code Playgroud)

不是一个合适的解决方案,因为它假定字符串中没有出现某个字符或字符序列.

注意使用它Pattern.quote来防止word被方法解释为正则表达式语法replaceAll.

解决方案3:正则表达式 \G

public String plusOut(String str, String word) {
  word = java.util.regex.Pattern.quote(word);
  return str.replaceAll("\\G((?:" + word + ")*+).", "$1+");
}
Run Code Online (Sandbox Code Playgroud)

构建正则表达式\G((?:word)*+).,它或多或少地做了解决方案1正在做的事情:

  • \G 确保匹配从上一场比赛的开始处开始
  • ((?:word)*+)选出0或更多的实例word- 如果有的话,这样我们就可以将它们保留在替换中$1.这里的关键是占有量词*+,它强制正则表达式保留word它找到的任何实例.否则,当word出现在字符串末尾时,正则表达式将无法正常工作,因为正则表达式回溯匹配.
  • .将不会成为任何一部分word,因为前一部分已经连续出现word并且不允许回溯.我们将替换它+

解决方案4:流媒体

public String plusOut(String str, String word) {
  return String.join(word, 
    Arrays.stream(str.split(java.util.regex.Pattern.quote(word), -1))
      .map((String s) -> s.replaceAll("(?s:.)", "+"))
      .collect(Collectors.toList()));
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是拆分字符串word,对其余部分进行替换,然后word使用using String.join方法将它们连接起来.

  • 与上面相同,我们需要Pattern.quote避免splitword正则表达式解释为正则表达式.由于split默认情况下会删除数组末尾的空字符串,因此我们需要-1在第二个参数中使用split这些空字符串.
  • 然后我们从数组中创建一个流,并将其余部分替换为字符串+.在Java 11中,我们可以使用s -> String.repeat(s.length()).
  • 剩下的就是将Stream转换为Iterable(在这种情况下为List)并将它们连接起来以获得结果


Cer*_*nce 6

这比你最初想象的要复杂一点,因为你不仅需要匹配字符,而且缺少特定的短语 - 否定的字符集是不够的.如果字符串是123,您将需要:

(?<=^|123)(?!123).*?(?=123|$)
Run Code Online (Sandbox Code Playgroud)

https://regex101.com/r/EZWMqM/1/

即 - 查看字符串开头或"123",确保当前位置后面没有123,然后懒惰重复任何字符,直到前瞻符合"123"或字符串结尾.这将匹配不在"123"子字符串中的所有字符.然后,您需要用a替换每个字符+,之后您可以使用appendReplacementa和a StringBuffer来创建结果字符串:

String inputPhrase = "123";
String inputStr = "abc123efg123123hij";
StringBuffer resultString = new StringBuffer();
Pattern regex = Pattern.compile("(?<=^|" + inputPhrase + ")(?!" + inputPhrase + ").*?(?=" + inputPhrase + "|$)");
Matcher m = regex.matcher(inputStr);
while (m.find()) {
    String replacement = m.group(0).replaceAll(".", "+");
    m.appendReplacement(resultString, replacement);
}
m.appendTail(resultString);
System.out.println(resultString.toString());
Run Code Online (Sandbox Code Playgroud)

输出:

+++123+++123123+++
Run Code Online (Sandbox Code Playgroud)

请注意,如果inputPhrase可以包含正则表达式中具有特殊含义的字符,则必须在连接到模式之前先将它们转义.