[英]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.