Java regex:将所有字符替换为“ +”,但给定字符串的实例除外

时间:2018-09-13 04:59:35

标签: java regex

我有以下问题指出

  

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

例如,如果给定的字符串为abc123efg,并且他们希望我替换除123的每个实例之外的所有字符,则它将变为+++123+++

我认为正则表达式可能是最好的选择,我想到了这个。

str.replaceAll("[^str]","+") 

其中str是一个变量,但是它不允许我在不使用引号的情况下使用该方法。如果我只想替换可变字符串str,该怎么办?我使用手动键入的字符串来运行它并且可以在该方法上使用,但是我可以只输入一个变量吗?

到目前为止,我相信它正在寻找字符串“ str”而不是变量字符串。

除了两个:(

enter image description here

未完成的测试用例列表:

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"

6 个答案:

答案 0 :(得分:14)

看起来这是CodingBat上的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来防止word方法将replaceAll解释为正则表达式。

解决方案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来避免splitword解释为正则表达式。由于默认情况下,split会在数组末尾删除空字符串,因此我们需要在第二个参数中使用-1来使split保留这些空字符串。
  • 然后,我们从数组中创建一个流,并将其余部分替换为+的字符串。在Java 11中,我们可以改用s -> String.repeat(s.length())
  • 其余的只是将Stream转换为Iterable(在这种情况下为List),然后将它们加入以获得结果

答案 1 :(得分:6)

这比您最初想的要棘手,因为您不仅需要匹配字符,而且缺少特定的短语-否定的字符集不够。如果字符串是123,则需要:

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<div id="app">
<div style="margin-bottom: 30px;">You have selected "{{ parentVar }}"</div>
<label>Test Autocomplete</label>
<autocomplete v-model="parentVar" :preset-val="parentVar" :items="autoOptions" />


</div>

<template id="autocomplete_template">
  <div class="autocomplete">
    <input type="text" @input="onChange" v-model="search" @keyup.down="onArrowDown" @keyup.up="onArrowUp" @keyup.enter="onEnter" class="form-control" />
    <ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results">
      <li class="loading" v-if="isLoading">
        Loading results...
      </li>
      <li v-else v-for="(result, i) in results" :key="i" @click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
        {{ result }}
      </li>
    </ul>
  </div>  
</template>

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

即-在字符串或“ 123”的开头后面查找,确保当前位置后没有123,然后延迟重复任何字符,直到超前匹配“ 123”或字符串的末尾。这将匹配不在“ 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+++ 在正则表达式中可以包含具有特殊含义的字符,则在连接到模式之前必须先对其进行转义。

答案 2 :(得分:2)

您可以一行完成:

input = input.replaceAll("((?:" + str + ")+)?(?!" + str + ").((?:" + str + ")+)?", "$1+$2");

这可以选择捕获每个字符的两侧的“ 123”并将其放回原位(如果没有“ 123”则为空白):

答案 3 :(得分:1)

因此,与其提供一个与缺少字符串匹配的正则表达式,不如说它是正则表达式。我们不妨只匹配所选的短语,然后在跳过的字符数后附加replies=[] non_bmp_map = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd) for full_tweets in tweepy.Cursor(api.user_timeline,screen_name='tartecosmetics',timeout=999999).items(10): for tweet in tweepy.Cursor(api.search,q='to:'+'tartecosmetics',result_type='recent',timeout=999999).items(1000): if hasattr(tweet, 'in_reply_to_status_id_str'): if (tweet.in_reply_to_status_id_str==full_tweets.id_str): replies.append(tweet.text) print("Tweet :",full_tweets.text.translate(non_bmp_map)) for elements in replies: print("Replies :",elements) replies.clear()

+

答案 4 :(得分:1)

要完成这项工作,您需要使用图案的野兽。假设您以以下测试用例为例:

plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"

您需要做的是在模式中构建一系列子句以一次匹配一个字符:

  • 不是“ X”,“ Y”或“ Z”的任何字符-[^XYZ]
  • 任何“ X”后都不是“ YZ”-X(?!YZ)
  • 任何不以“ X”开头的“ Y”-(?<!X)Y
  • 任何“ Y”后都不是“ Z”-Y(?!Z)
  • 任何不以“ XY”开头的“ 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”)的排除项。正则表达式绝对不是正确的工具,但我认为这是可能的。闲逛后,我不确定是否存在可以使这项工作有效的模式。

答案 5 :(得分:1)

解决方案中的问题是您放置了一组实例字符串str.replaceAll("[^str]","+"),它将排除变量str中的任何字符,并且不能解决问题

EX :当您尝试str.replaceAll("[^XYZ]","+")时,它将从替换方法中排除字符X,字符Y和字符Z的任何组合因此您将获得“ ++XY+++XYZ”。

实际上,您应该在str.replaceAll中排除字符序列。

您可以通过使用{strong>捕获组这样的字符来实现,例如(XYZ),然后使用负向超前来匹配不包含字符序列的字符串:{ {1}}

请检查此solution,以获取有关此问题的更多信息,但您应该知道,找到正则表达式直接执行此操作可能会很复杂。

我找到了两个解决此问题的简单方法:

解决方案1 ​​

您可以实现一种方法,用给定字符串实例除外的所有字符替换为'^((?!XYZ).)*$'

+

注意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 } } 此if语句将从str.substring(i, str.length()).indexOf(exWord) + i的替换过程中排除exWord的任何实例字符串。

输出

str

解决方案2

您可以使用 ReplaceAll 方法尝试这种方法,它不需要任何复杂的正则表达式:

+++++++XYZ

注意:仅当您的输入字符串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