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 : (
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"
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 deword
- 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 leword
apparaissent à la fin de la chaîne, comme dans l'expression rationnelle revient pour correspondre.
.
ne sera pas une partie de toutword
, puisque la partie précédente indique déjà toutes les apparitions consécutives deword
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
à évitersplit
interpreter leword
comme regex. Depuissplit
par défaut supprime la chaîne vide à la fin du tableau, nous avons besoin d'utiliser-1
dans le second paramètre pour fairesplit
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 utilisers -> 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
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.
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"):
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('+');
}
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();
}
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.
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 = "++"
.