Disons que j'ai le code suivant:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);
Une fois ce code exécuté, la valeur de story
sera"Once upon a time, there was a foo and a foo."
Un problème similaire se produit si je les remplace dans l'ordre inverse:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);
La valeur de story
sera"Once upon a time, there was a bar and a bar."
Mon objectif est de story
devenir "Once upon a time, there was a bar and a foo."
Comment pourrais-je accomplir cela?
swap(String s1, String s2, String s3)
qui permute toutes les occurrences des2
withs3
, et vice versa.Réponses:
Utilisez la
replaceEach()
méthode d' Apache Commons StringUtils :la source
null
est passé, cependant.Vous utilisez une valeur intermédiaire (qui n'est pas encore présente dans la phrase).
En réponse à la critique: si vous utilisez une chaîne assez grande et peu commune comme zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddjhodfqsqc, nvùq ^ µù; d & € sdq: d:;) àçàçlala où il est peu probable que je débatte de ce point qu'un utilisateur entrera jamais ceci. La seule façon de savoir si un utilisateur le fera est de connaître le code source et à ce stade, vous êtes avec un tout autre niveau de soucis.
Oui, il existe peut-être des méthodes regex sophistiquées. Je préfère quelque chose de lisible dont je sais qu'il ne m'éclatera pas non plus.
Réitérant également l'excellent conseil donné par @David Conrad dans les commentaires :
la source
Vous pouvez essayer quelque chose comme ça, en utilisant
Matcher#appendReplacement
etMatcher#appendTail
:la source
foo
,bar
etstory
tous ont des valeurs inconnues?"foo"
et"bar"
les chaînes de remplacement que l'OP avait dans son code, mais le même type d'approche fonctionnerait bien même si ces valeurs ne sont pas connues (vous devrez utiliserif
/else if
au lieu d'unswitch
dans lewhile
-boucle).Pattern.quote
serait utile, ou\Q
et\E
.(foo)|(bar)
et de vérifier ensuitem.group(1) != null
, pour éviter de répéter les mots pour correspondre.Ce n'est pas un problème facile. Et plus vous avez de paramètres de remplacement de recherche, plus cela devient compliqué. Vous avez plusieurs options, dispersées sur la palette de moche-élégant, efficace-gaspillage:
Utilisation
StringUtils.replaceEach
depuis Apache Commons comme @AlanHay recommandé. C'est une bonne option si vous êtes libre d'ajouter de nouvelles dépendances dans votre projet. Vous pourriez avoir de la chance: la dépendance peut déjà être incluse dans votre projetUtilisez un espace réservé temporaire comme @Jeroen l'a suggéré et effectuez le remplacement en 2 étapes:
Ce n'est pas une bonne approche, pour plusieurs raisons: il faut s'assurer que les balises utilisées dans la première étape sont vraiment uniques; il effectue plus d'opérations de remplacement de chaîne que nécessaire
Construisez une expression régulière à partir de tous les modèles et utilisez la méthode avec
Matcher
etStringBuffer
comme suggéré par @arshajii . Ce n'est pas terrible, mais pas terrible non plus, car la construction de l'expression régulière est un peu hackish, et cela impliqueStringBuffer
ce qui s'est démodé il y a quelque temps en faveur deStringBuilder
.Utilisez une solution récursive proposée par @mjolka , en fractionnant la chaîne au niveau des modèles correspondants et en effectuant une récurrence sur les segments restants. C'est une solution fine, compacte et assez élégante. Sa faiblesse réside dans les opérations potentiellement nombreuses de sous-chaînes et de concaténation, et les limites de taille de pile qui s'appliquent à toutes les solutions récursives
Divisez le texte en mots et utilisez les flux Java 8 pour effectuer les remplacements avec élégance, comme @msandiford l'a suggéré, mais bien sûr, cela ne fonctionne que si vous êtes d'accord avec le fractionnement aux limites des mots, ce qui le rend inapproprié comme solution générale
Voici ma version, basée sur des idées empruntées à l'implémentation d' Apache . Ce n'est ni simple ni élégant, mais cela fonctionne et devrait être relativement efficace, sans étapes inutiles. En un mot, cela fonctionne comme ceci: recherchez à plusieurs reprises le modèle de recherche correspondant suivant dans le texte, et utilisez a
StringBuilder
pour accumuler les segments sans correspondance et les remplacements.Tests unitaires:
la source
Recherchez le premier mot à remplacer. Si c'est dans la chaîne, recurse sur la partie de la chaîne avant l'occurrence et sur la partie de la chaîne après l'occurrence.
Sinon, continuez avec le mot suivant à remplacer.
Une implémentation naïve pourrait ressembler à ceci
Exemple d'utilisation:
Production:
Une version moins naïve:
Malheureusement, Java
String
n'a pas deindexOf(String str, int fromIndex, int toIndex)
méthode. J'ai omis l'implémentation d'indexOf
ici car je ne suis pas certain qu'elle soit correcte, mais elle peut être trouvée sur ideone , avec quelques timings approximatifs de diverses solutions publiées ici.la source
One-liner dans Java 8:
?<=
,?=
): http://www.regular-expressions.info/lookaround.htmlla source
Voici une possibilité de flux Java 8 qui pourrait être intéressante pour certains:
Voici une approximation du même algorithme en Java 7:
la source
Si vous souhaitez remplacer des mots dans une phrase qui sont séparés par un espace comme indiqué dans votre exemple, vous pouvez utiliser cet algorithme simple.
Si le fractionnement sur l'espace n'est pas acceptable, on peut suivre cet algorithme alternatif. Vous devez d'abord utiliser la chaîne la plus longue. Si les chaînes sont foo et foo, vous devez d'abord utiliser foo, puis foo.
la source
Voici une réponse moins compliquée avec Map.
Et la méthode s'appelle
Le résultat est: génial est Raffy, Raffy Raffy est génial génial
la source
replaced.replaceAll("Raffy", "Barney");
après cela le rendra legen ... attendez-le; Dary !!!Si vous souhaitez pouvoir gérer plusieurs occurrences des chaînes de recherche à remplacer, vous pouvez le faire facilement en fractionnant la chaîne sur chaque terme de recherche, puis en la remplaçant. Voici un exemple:
la source
Vous pouvez atteindre votre objectif avec le bloc de code suivant:
Il remplace les mots quel que soit l'ordre. Vous pouvez étendre ce principe dans une méthode utilitaire, comme:
Qui serait consommé comme:
la source
Cela fonctionne et est simple:
Vous l'utilisez comme ceci:
Remarque: cela compte sur les chaînes ne contenant pas de caractère
\ufdd0
, qui est un caractère réservé en permanence à un usage interne par Unicode (voir http://www.unicode.org/faq/private_use.html ):Je ne pense pas que ce soit nécessaire, mais si vous voulez être absolument sûr, vous pouvez utiliser:
la source
Échange d'une seule occurrence
S'il n'y a qu'une seule occurrence de chacune des chaînes échangeables dans l'entrée, vous pouvez effectuer les opérations suivantes:
Avant de procéder à tout remplacement, récupérez les indices des occurrences des mots. Après cela, nous ne remplaçons que le mot trouvé à ces index, et non toutes les occurrences. Cette solution utilise
StringBuilder
et ne produit pas des intermédiairesString
commeString.replace()
.Une chose à noter: si les mots échangeables ont des longueurs différentes, après le premier remplacement, le deuxième index peut changer (si le 1er mot se produit avant le 2ème) exactement avec la différence des 2 longueurs. Donc, aligner le deuxième index garantira que cela fonctionne même si nous échangeons des mots de différentes longueurs.
Permutation du nombre arbitraire d'occurrences
Comme dans le cas précédent, nous allons d'abord collecter les index (occurrences) des mots, mais dans ce cas, il s'agira d'une liste d'entiers pour chaque mot, pas seulement un
int
. Pour cela, nous utiliserons la méthode utilitaire suivante:Et en utilisant cela, nous remplacerons les mots par l'autre en diminuant l'indice (ce qui peut nécessiter d'alterner entre les 2 mots échangeables) afin que nous n'ayons même pas à corriger les index après un remplacement:
la source
indexOf
correspond peut ne pas avoir la même longueur que la chaîne de recherche grâce aux idiosyncrasies de l'équivalence de chaîne Unicode.String
est un tableau de caractères et non un tableau d'octets. Toutes les méthodes deString
etStringBuilder
fonctionnent sur des caractères non sur des octets, qui sont "sans encodage". Ainsi, lesindexOf
correspondances ont exactement la même longueur (en caractères) que les chaînes de recherche.ä
peut être codé comme un point de code unique ou commea
suivi d'une combinaison¨
. Il existe également des points de code qui sont ignorés, tels que les (non) joints de largeur zéro. Peu importe si la chaîne se compose d'octets, de caractères ou autre, mais quelles règles de comparaisonindexOf
utilisent. Il peut utiliser simplement unité de code par comparaison d'unité de code ("Ordinal") ou il peut implémenter l'équivalence unicode. Je ne sais pas lequel java a choisi."ab\u00ADc".IndexOf("bc")
renvoie1
en .net faisant correspondre la chaînebc
de deux caractères à une chaîne de trois caractères."ab\u00ADc".indexOf("bc")
renvoie-1
ce qui signifie"bc"
n'a pas été trouvé dans"ab\u00ADc"
. Il reste donc qu'en Java, l'algorithme ci-dessus fonctionne, lesindexOf()
correspondances ont exactement la même longueur (en caractères) que les chaînes de recherche etindexOf()
ne signale les correspondances que si les séquences de caractères (points de code) correspondent.Il est facile d'écrire une méthode pour faire cela en utilisant
String.regionMatches
:Essai:
Production:
Ce n'est pas immédiatement évident, mais une fonction comme celle-ci peut toujours dépendre de l'ordre dans lequel les remplacements sont spécifiés. Considérer:
Production:
Mais inversez les remplacements:
Production:
Oups! :)
Par conséquent, il est parfois utile de s'assurer de rechercher la correspondance la plus longue (comme le fait la
strtr
fonction PHP , par exemple). Cette version de la méthode fera cela:Notez que les méthodes ci-dessus sont sensibles à la casse. Si vous avez besoin d'une version insensible à la casse, il est facile de modifier ce qui précède car
String.regionMatches
peut prendre unignoreCase
paramètre.la source
Si vous ne voulez pas de dépendances, vous pouvez simplement utiliser un tableau qui n'autorise qu'une seule modification. Ce n'est pas la solution la plus efficace, mais elle devrait fonctionner.
Ensuite, cela fonctionnerait.
la source
Vous effectuez plusieurs opérations de recherche-remplacement sur l'entrée. Cela produira des résultats indésirables lorsque les chaînes de remplacement contiennent des chaînes de recherche. Prenons l'exemple de foo-> bar, bar-foo, voici les résultats pour chaque itération:
Vous devez effectuer le remplacement en une seule itération sans revenir en arrière. Une solution de force brute est la suivante:
Une fonction telle que
String.indexOfAny(String[]) -> int[]{index, whichString}
serait utile. Voici un exemple (pas le plus efficace):Quelques tests:
Démo sur IDEONE
Démo sur IDEONE, code alternatif
la source
Vous pouvez toujours le remplacer par un mot dont vous êtes sûr qu'il n'apparaîtra nulle part ailleurs dans la chaîne, puis effectuer le deuxième remplacement plus tard:
Notez que cela ne fonctionnera pas correctement si
"StringYouAreSureWillNeverOccur"
cela se produit.la source
Envisagez d'utiliser StringBuilder
Ensuite, stockez l'index à l'endroit où chaque chaîne doit commencer. Si vous utilisez un caractère d'espace réservé à chaque position, supprimez-le et insérez la chaîne d'utilisateurs. Vous pouvez ensuite mapper la position de fin en ajoutant la longueur de chaîne à la position de départ.
la source
Ce que je ne peux partager, c'est ma propre méthode.
Vous pouvez utiliser un temporaire
String temp = "<?>";
ouString.Format();
Ceci est mon exemple de code créé dans l'application console via c # - "Idée seulement, pas de réponse exacte" .
Ou vous pouvez également utiliser le
String.Format();
Production:
time upon a Once, there was a bar and a foo.
la source
temp
de"_"
en<?>
. Mais si nécessaire, ce qu'il peut faire est d'ajouter un autre paramètre à la méthode qui changera la température. - "il vaut mieux rester simple non?"Voici ma version, qui est basée sur des mots:
la source
Petite manière délicate mais vous devez faire quelques vérifications supplémentaires.
1. convertir la chaîne en tableau de caractères
2. bouclez sur temp et remplacez
foo
parbar
etbar
parfoo
car il n'y a aucune chance d'obtenir à nouveau une chaîne remplaçable.la source
Eh bien, la réponse la plus courte est ...
la source
En utilisant la réponse trouvée ici, vous pouvez trouver toutes les occurrences des chaînes que vous souhaitez remplacer.
Ainsi, par exemple, vous exécutez le code dans la réponse SO ci-dessus. Créez deux tables d'index (disons que bar et foo n'apparaissent pas une seule fois dans votre chaîne) et vous pouvez travailler avec ces tables pour les remplacer dans votre chaîne.
Maintenant, pour remplacer sur des emplacements d'index spécifiques, vous pouvez utiliser:
Alors que
pos
c'est l'index où vos chaînes commencent (à partir des tables d'index que j'ai citées ci-dessus). Disons que vous avez créé deux tables d'index pour chacun. Appelons-lesindexBar
etindexFoo
.Maintenant, en les remplaçant, vous pouvez simplement exécuter deux boucles, une pour chaque remplacement que vous souhaitez effectuer.
De même une autre boucle pour
indexFoo
.Cela n'est peut-être pas aussi efficace que d'autres réponses ici, mais c'est plus simple à comprendre que Maps ou d'autres choses.
Cela vous donnerait toujours le résultat souhaité et pour plusieurs occurrences possibles de chaque chaîne. Tant que vous stockez l'index de chaque occurrence.
De plus, cette réponse ne nécessite aucune récursivité ni aucune dépendance externe. En ce qui concerne la complexité, elle est probablement O (n au carré), alors que n est la somme des occurrences des deux mots.
la source
J'ai développé ce code résoudra le problème:
Dans l'utilisation principale
change(story,word2,word1).
la source
la source