[英]Java regex: Replace all characters with `+` except instances of a given string
我有以下问题说明
使用
+
符号替换字符串中的所有字符,但方法中给定字符串的实例除外
所以例如,如果给出的字符串是abc123efg
并且他们希望我替换除123
每个实例之外的每个字符,那么它将变为+++123+++
。
我认为正则表达式可能是最好的,我想出了这个。
str.replaceAll("[^str]","+")
其中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"
看起来这是plusOut
上的plusOut
问题。
我有3个解决这个问题的方法,并为了好玩而写了一个新的流媒体解决方案。
从输入字符串中创建一个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();
}
这可能是初学者程序员的预期答案,尽管假设字符串不包含任何星体平面字符,它将由2个字符而不是1表示。
public String plusOut(String str, String word) {
return str.replaceAll(java.util.regex.Pattern.quote(word), "@").replaceAll("[^@]", "+").replaceAll("@", word);
}
不是一个合适的解决方案,因为它假定字符串中没有出现某个字符或字符序列。
请注意使用Pattern.quote
来防止word
被replaceAll
方法解释为正则表达式语法。
\\G
表达式 public String plusOut(String str, String word) {
word = java.util.regex.Pattern.quote(word);
return str.replaceAll("\\G((?:" + word + ")*+).", "$1+");
}
构造正则表达式\\G((?:word)*+).
,它或多或少地做了解决方案1正在做的事情:
\\G
确保匹配从上一场比赛的开始处开始 ((?:word)*+)
选出0个或更多的word
实例 - 如果有的话,这样我们就可以用$1
替换它们。 这里的关键是占有量词*+
,它强制正则表达式保留它找到的word
任何实例。 否则,当word
出现在字符串的末尾时,正则表达式将无法正常工作,因为正则表达式回溯匹配.
.
不会成为任何word
一部分,因为前一部分已经连续出现word
并且不允许回溯。 我们将用+
替换它 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()));
}
我们的想法是通过拆分串word
,做剩下的更换,和他们一起回来word
使用String.join
方法。
Pattern.quote
来避免split
word
解释为正则表达式。 由于默认情况下split
会删除数组末尾的空字符串,因此我们需要在第二个参数中使用-1
来split
这些空字符串。 +
字符串。 在Java 11中,我们可以使用s -> String.repeat(s.length())
代替。 这比你最初想象的要复杂一点,因为你不仅需要匹配字符 ,而且缺少特定的短语 - 否定的字符集是不够的。 如果字符串是123,您将需要:
(?<=^|123)(?!123).*?(?=123|$)
https://regex101.com/r/EZWMqM/1/
即 - 查看字符串开头或“123”,确保当前位置后面没有123,然后懒惰重复任何字符,直到前瞻符合“123”或字符串结尾。 这将匹配不在“123”子字符串中的所有字符。 然后,您需要用+
替换每个字符,之后您可以使用appendReplacement
和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());
输出:
+++123+++123123+++
请注意,如果inputPhrase
可以包含正则表达式中具有特殊含义的字符,则必须在连接到模式之前先将它们转义。
你可以在一行中完成:
input = input.replaceAll("((?:" + str + ")+)?(?!" + str + ").((?:" + str + ")+)?", "$1+$2");
这可选地捕获每个字符两侧的“123”并将它们放回(如果没有“123”则为空白):
因此,而不是提出一个匹配缺少字符串的正则表达式。 我们也可以只匹配所选短语并附加+
跳过的字符数。
StringBuilder sb = new StringBuilder();
Matcher m = Pattern.compile(Pattern.quote(str)).matcher(input);
while (m.find()) {
for (int i = 0; i < m.start(); i++) sb.append('+');
sb.append(str);
}
int remaining = input.length() - sb.length();
for (int i = 0; i < remaining; i++) {
sb.append('+');
}
绝对只是为了它的乐趣,使用CharBuffer
的解决方案(出乎意料的是它花了我原先希望的更多):
private static String plusOutCharBuffer(String input, String match) {
int size = match.length();
CharBuffer cb = CharBuffer.wrap(input.toCharArray());
CharBuffer word = CharBuffer.wrap(match);
int x = 0;
for (; cb.remaining() > 0;) {
if (!cb.subSequence(0, size < cb.remaining() ? size : cb.remaining()).equals(word)) {
cb.put(x, '+');
cb.clear().position(++x);
} else {
cb.clear().position(x = x + size);
}
}
return cb.clear().toString();
}
要完成这项工作,你需要一个模式的野兽。 假设您正在使用以下测试用例作为示例:
plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"
你需要做的是在你的模式中构建一系列子句,以便一次匹配一个字符:
[^XYZ]
X(?!YZ)
(?<!X)Y
Y(?!Z)
(?<!XY)Z
可以在此处找到此替换的示例: https : //regex101.com/r/jK5wU3/4
下面是一个如何工作的例子(大多数情况下没有优化,但它有效):
import java.util.regex.Pattern;
public class Test {
public static void plusOut(String text, String exclude) {
StringBuilder pattern = new StringBuilder("");
for (int i=0; i<exclude.length(); i++) {
Character target = exclude.charAt(i);
String prefix = (i > 0) ? exclude.substring(0, i) : "";
String postfix = (i < exclude.length() - 1) ? exclude.substring(i+1) : "";
// add the look-behind (?<!X)Y
if (!prefix.isEmpty()) {
pattern.append("(?<!").append(Pattern.quote(prefix)).append(")")
.append(Pattern.quote(target.toString())).append("|");
}
// add the look-ahead X(?!YZ)
if (!postfix.isEmpty()) {
pattern.append(Pattern.quote(target.toString()))
.append("(?!").append(Pattern.quote(postfix)).append(")|");
}
}
// add in the other character exclusion
pattern.append("[^" + Pattern.quote(exclude) + "]");
System.out.println(text.replaceAll(pattern.toString(), "+"));
}
public static void main(String [] args) {
plusOut("12xy34", "xy");
plusOut("12xy34", "1");
plusOut("12xy34xyabcxy", "xy");
plusOut("abXYabcXYZ", "ab");
plusOut("abXYabcXYZ", "abc");
plusOut("abXYabcXYZ", "XY");
plusOut("abXYxyzXYZ", "XYZ");
plusOut("--++ab", "++");
plusOut("aaxxxxbb", "xx");
plusOut("123123", "3");
}
}
更新:即使这不起作用,因为它不能处理只是重复字符的排除,如“xx”。 正则表达式绝对不是正确的工具,但我认为它可能是正确的。 在探索之后,我不太确定甚至存在可能使这项工作的模式。
您的解决方案中存在一组实例字符串str.replaceAll("[^str]","+")
,它将从变量str
排除任何字符并且无法解决您的问题
EX :当你尝试str.replaceAll("[^XYZ]","+")
它会从你的替换方法中排除字符X
,字符Y
和字符Z
任意组合,这样你就会得到“ ++XY+++XYZ
”。
实际上你应该在str.replaceAll
排除一系列字符。
您可以使用捕获字符组 (如(XYZ)
然后使用负向前导来匹配不包含字符序列的字符串: ^((?!XYZ).)*$
检查此解决方案以获取有关此问题的更多信息,但您应该知道找到正则表达式直接执行此操作可能很复杂。
我找到了两个解决这个问题的简单方法:
解决方案1 :
除了给定字符串的实例外,您可以实现一个用' +
'替换所有字符的方法:
String exWord = "XYZ";
String str = "abXYxyzXYZ";
for(int i = 0; i < str.length(); i++){
// exclude any instance string of exWord from replacing process in str
if(str.substring(i, str.length()).indexOf(exWord) + i == i){
i = i + exWord.length()-1;
}
else{
str = str.substring(0,i) + "+" + str.substring(i+1);//replace each character with '+' symbol
}
}
注意 : str.substring(i, str.length()).indexOf(exWord) + i
这个if语句将排除任何exWord
实例字符串替换str
进程。
输出 :
+++++++XYZ
解决方案2 :
您可以使用ReplaceAll方法尝试此方法,它不需要任何复杂的正则表达式:
String exWord = "XYZ";
String str = "abXYxyzXYZ";
str = str.replaceAll(exWord,"*"); // replace instance string with * symbol
str = str.replaceAll("[^*]","+"); // replace all characters with + symbol except *
str = str.replaceAll("\\*",exWord); // replace * symbol with instance string
注意 :仅当输入字符串str
不包含任何*
符号时,此解决方案才有效。
此外,您应该在短语实例字符串exWord
中的正则表达式中转义具有特殊含义的任何字符,例如: exWord = "++"
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.