Java regex: remplacer tous les caractères par ' + ' sauf les instances d'une chaîne donnée

j'ai le problème suivant laquelle les états

remplacer tous les caractères d'une chaîne par + symbole à l'exception de cas de la chaine de caractères donnée dans la méthode

donc par exemple si la chaîne donnée était abc123efg et ils veulent me faire remplacer tous les caractères à l'exception de chaque instance de 123 alors il deviendrait +++123+++.

j'ai pensé qu'une expression régulière est probablement la meilleure pour ça et j'ai trouvé ce.

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

où str est une variable, mais c'est ne pas me laisser utiliser la méthode sans la mettre dans les citations. Si je veux juste remplacer la chaîne de caractères str, Comment puis-je le faire? Je l'ai lancé avec la chaîne tapée manuellement et il a fonctionné sur la méthode, mais puis-je juste entrer une variable?

a partir de Maintenant je crois qu'il cherche la chaîne "str" et non la chaîne variable.

Voici la sortie sa droite pour tant de cas à l'exception de deux : (

enter image description here

Liste des cas de test:

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"
22
demandé sur Rich 2018-09-13 07:59:34

7 réponses

Regarde comme c'est le plusOut problème sur CodingBat.

j'ai eu 3 solutions à ce problème, et j'ai écrit une nouvelle solution de streaming juste pour le plaisir.

Solution 1: boucle et vérifier

Créer un StringBuilder de la chaîne d'entrée et vérifier le mot dans chaque position. Remplacer le caractère si ne correspond pas, et de sauter la longueur du mot, le cas échéant.

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();
}

C'est probablement la réponse attendue pour un programmeur débutant, bien qu'il y ait une hypothèse que la chaîne ne contient aucun caractère plan astral, qui serait représenté par 2 char au lieu de 1.

Solution 2: Remplacer le mot par un marqueur, remplacer le reste, puis restaurer le mot

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

Pas une bonne solution car elle suppose qu'un certain caractère ou d'une séquence de caractère n'apparaît pas dans la chaîne.

notez l'utilisation de Pattern.quote pour empêcher l' word étant interprété comme syntaxe regex par replaceAll méthode.

Solution 3: Regex with \G

public String plusOut(String str, String word) {
  word = java.util.regex.Pattern.quote(word);
  return str.replaceAll("\G((?:" + word + ")*+).", "+");
}

Construire des regex \G((?:word)*+)., ce qui fait plus ou moins ce que fait la solution 1:

  • \G s'assure que la correspondance commence là où la correspondance précédente se termine
  • ((?:word)*+) indique 0 instance ou plus de word - le cas échéant, afin que nous puissions les garder dans le remplacement . La clé ici est le possessif quantificateur *+, ce qui force le regex à garder toutes les instances de l' word il trouve. Sinon, le regex ne fonctionnera pas correctement quand le word apparaissent à la fin de la chaîne, comme dans l'expression rationnelle revient pour correspondre .
  • . ne sera pas une partie de tout word, puisque la partie précédente indique déjà toutes les apparitions consécutives de word et interdire de revenir en arrière. Nous allons le remplacer par +

Solution 4: Streaming

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()));
}

L'idée est de séparer la chaîne par word, ne le remplacement sur le reste, et joindre avec word en utilisant String.join méthode.

  • comme ci-dessus, nous avons besoin Pattern.quote à éviter split interpreter le word comme regex. Depuis split par défaut supprime la chaîne vide à la fin du tableau, nous avons besoin d'utiliser -1 dans le second paramètre pour faire split laissez ces cordes vides en paix.
  • ensuite nous créons un flux hors du tableau et remplaçons le reste par des chaînes de +. Dans Java 11, on peut utiliser s -> String.repeat(s.length()) à la place.
  • le reste est simplement en train de convertir le flux en un flux itérable (liste dans ce cas) et de les rejoindre pour le résultat
14
répondu nhahtdh 2018-09-13 06:21:19

C'est un peu plus compliqué que vous pourriez penser, parce que vous n'avez pas besoin juste de faire correspondre caractères, mais l'absence de phrase - un jeu de caractères nié ne suffit pas. Si la chaîne est de 123, vous avez besoin de:

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

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

Qu'est - lookbehind pour le début de la chaîne ou "123", assurez-vous que la position actuelle n'est pas suivie par 123, puis paresseux-répéter tout caractère jusqu'à ce anticipation correspond à "123" ou la fin de la chaîne. Cela correspondra à tous les caractères qui ne sont pas dans une chaîne "123". Ensuite, vous devez remplacer chaque caractère avec un +, après quoi vous pouvez utiliser appendReplacement et StringBuffer pour créer la chaîne de résultat:

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());

Sortie:

+++123+++123123+++

Notez que si le inputPhrase peut contenir un caractère avec une signification particulière dans une expression régulière, vous devrez d'abord les échapper avant de concaténer dans le modèle.

6
répondu CertainPerformance 2018-09-13 05:57:07

Vous pouvez le faire en une seule ligne:

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

cette option Capture "123" de chaque côté de chaque caractère et les renvoie (un blanc s'il n'y a pas de "123"):

2
répondu Bohemian 2018-09-13 21:50:11

donc au lieu de trouver une expression régulière qui correspond à l'absence d'une chaîne. On pourrait tout aussi bien correspondre à la phrase sélectionnée et ajouter + le nombre de caractères omis.

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('+');
}
1
répondu xiaofeng.li 2018-09-13 05:55:34

Absolument juste pour le fun, une solution à l'aide de CharBuffer (inopinément, il a fallu beaucoup plus que ce que j'espérais au départ):

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();
}
1
répondu Eugene 2018-09-13 14:22:38

Pour faire ce travail vous avez besoin d'une bête de modèle. Supposons que vous opériez sur le cas de test suivant comme exemple:

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

Ce que vous devez faire est d'établir une série de clauses dans votre modèle pour correspondre à un seul caractère à la fois:

  • tout caractère qui N'est PAS" X"," Y "ou" Z"--[^XYZ]
  • Tout sur le "X" n'est pas suivie par "YZ" -- X(?!YZ)
  • Any "Y" not precededed by "X"-- (?<!X)Y
  • tout " Y " Non suivi de" Z"-- Y(?!Z)
  • Tout sur le "Z" non précédé de "XY" -- (?<!XY)Z

Un exemple de ce remplacement peut être trouvé ici: https://regex101.com/r/jK5wU3/4

Voici un exemple de comment cela fonctionne (certainement pas optimisé, mais il fonctionne):

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");

    }

}

mise à jour: même cela ne fonctionne pas tout à fait parce qu'il ne peut pas traiter des exclusions qui ne sont que des caractères répétés, comme "xx". Les expressions régulières sont les plus certainement pas le bon outil pour ça, mais j'ai pensé que ça pourrait être possible. Après avoir fouiné, Je ne suis pas sûr qu'il existe un modèle qui pourrait faire marcher ça.

1
répondu Daedalus 2018-09-21 01:20:53

le problème dans votre solution que vous mettez un ensemble de chaîne d'instance str.replaceAll("[^str]","+") qui exclura tout caractère de la variable str et cela ne résoudra pas votre problème

EX: lorsque vous essayez str.replaceAll("[^XYZ]","+") il exclura toute combinaison de caractère X , le caractère Y et de caractère Z à partir de votre remplacement de la méthode de sorte que vous aurez "++XY+++XYZ".

en fait, vous devriez exclure une séquence de caractères à la place str.replaceAll.

Vous pouvez le faire en utilisant groupe de caractères comme (XYZ) alors utilisez un anticipation négatif pour correspondre à une chaîne qui ne contient pas les caractères de la séquence : ^((?!XYZ).)*$

Cochez cette solution pour plus d'informations sur ce problème, mais vous devez savoir qu'il peut être compliqué de trouver l'expression régulière à le faire directement.

j'en ai trouvé deux solutions simples à ce problème :

Solution 1:

vous pouvez implémenter une méthode pour remplacer tous les caractères par'+' sauf l'instance de la chaîne donnée:

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
    }
}             

Remarque::str.substring(i, str.length()).indexOf(exWord) + i cette instruction if exclura toute chaîne d'instances de exWord remplacement de processus str.

Sortie:

+++++++XYZ

Solution 2:

vous pouvez essayer cette approche en utilisant ReplaceAll méthode et il n'a pas besoin d'expression régulière complexe:

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

Remarque:: cette solution ne fonctionnera que si votre chaîne de caractèresstr ne contient pas d' * symbole.

vous devez également échapper à tout caractère avec une signification spéciale dans une expression régulière dans la phrase chaîne d'instancesexWord comme : exWord = "++".

1
répondu Oghli 2018-09-23 06:25:07