簡體   English   中英

Java正則表達式:除了給定字符串的實例外,用`+`替換所有字符

[英]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個解決這個問題的方法,並為了好玩而寫了一個新的流媒體解決方案。

解決方案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();
}

這可能是初學者程序員的預期答案,盡管假設字符串不包含任何星體平面字符,它將由2個字符而不是1表示。

解決方案2:用標記替換單詞,替換其余單詞,然后恢復單詞

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

不是一個合適的解決方案,因為它假定字符串中沒有出現某個字符或字符序列。

請注意使用Pattern.quote來防止wordreplaceAll方法解釋為正則表達式語法。

解決方案3:使用\\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並且不允許回溯。 我們將用+替換它

解決方案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()));
}

我們的想法是通過拆分串word ,做剩下的更換,和他們一起回來word使用String.join方法。

  • 與上面相同,我們需要Pattern.quote來避免split word解釋為正則表達式。 由於默認情況下split會刪除數組末尾的空字符串,因此我們需要在第二個參數中使用-1split這些空字符串。
  • 然后我們從數組中創建一個流,並將其余部分替換為+字符串。 在Java 11中,我們可以使用s -> String.repeat(s.length())代替。
  • 其余的只是將Stream轉換為Iterable(在本例中為List)並將它們連接起來以獲得結果

這比你最初想象的要復雜一點,因為你不僅需要匹配字符 ,而且缺少特定的短語 - 否定的字符集是不夠的。 如果字符串是123,您將需要:

(?<=^|123)(?!123).*?(?=123|$)

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

即 - 查看字符串開頭或“123”,確保當前位置后面沒有123,然后懶惰重復任何字符,直到前瞻符合“123”或字符串結尾。 這將匹配不在“123”子字符串中的所有字符。 然后,您需要用+替換每個字符,之后您可以使用appendReplacementStringBuffer來創建結果字符串:

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"

你需要做的是在你的模式中構建一系列子句,以便一次匹配一個字符:

  • 任何不是“X”,“Y”或“Z”的字符 - [^XYZ]
  • 任何“X”后面跟着“YZ” - X(?!YZ)
  • 任何“Y”前面都沒有“X” - (?<!X)Y
  • 任何“Y”后面跟着“Z” - Y(?!Z)
  • 任何“Z”前面沒有“XY” - (?<!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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM