web-dev-qa-db-fra.com

Comment puis-je remplacer deux chaînes de manière à ce que l'une ne remplace pas l'autre?

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 ai remplacés 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 but est de transformer story en "Once upon a time, there was a bar and a foo." Comment pourrais-je accomplir cela?

159
Pikamander2

Utilisez la méthode replaceEach() de Apache Commons StringUtils :

StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})
87
Alan Hay

Vous pouvez essayer quelque chose comme ça en utilisant Matcher#appendReplacement et Matcher#appendTail :

String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";

Pattern p = Pattern.compile("foo|bar");
Matcher m = p.matcher(story);
StringBuffer sb = new StringBuffer();
while (m.find()) {
    /* do the swap... */
    switch (m.group()) {
    case "foo":
        m.appendReplacement(sb, Word1);
        break;
    case "bar":
        m.appendReplacement(sb, Word2);
        break;
    default:
        /* error */
        break;
    }
}
m.appendTail(sb);

System.out.println(sb.toString());
 Il était une fois un bar et un foo. 
33
arshajii

Ce n'est pas un problème facile. Et plus vous avez de paramètres de recherche-remplacement, plus cela devient délicat. Vous avez plusieurs options, dispersées sur la palette de laide-élégant, efficace-gaspiller:

  • Utilisation StringUtils.replaceEach de 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 pourrait déjà être incluse dans votre projet

  • Utilisez un espace réservé temporaire comme @ Jeroen suggéré et effectuez le remplacement en 2 étapes:

    1. Remplacer tous les modèles de recherche par une balise unique qui n'existe pas dans le texte d'origine
    2. Remplacer les espaces réservés par le remplacement de cible réelle

    Ce n’est pas une bonne approche pour plusieurs raisons: il faut s’assurer que les balises utilisées lors de la première étape sont vraiment uniques; il effectue plus d'opérations de remplacement de chaînes que nécessaire

  • Construisez une expression rationnelle à partir de tous les modèles et utilisez la méthode avec Matcher et StringBuffer comme suggéré par @ arshajii . Ce n'est pas terrible, mais pas terrible non plus, car la construction de la regex est une sorte de hackish et implique StringBuffer qui s'est démodé il y a quelque temps en faveur de StringBuilder.

  • Utilisez une solution récursive proposée par @ mjolka , en scindant la chaîne au niveau des modèles correspondants et en effectuant une récurrence sur les segments restants. C'est une bonne solution, compacte et assez élégante. Sa faiblesse réside dans le nombre potentiellement important d'opérations de sous-chaîne et de concaténation, ainsi que dans les limites de taille de pile qui s'appliquent à toutes les solutions récursives.

  • Divisez le texte en mots et utilisez Java 8 flux pour effectuer les remplacements avec élégance comme @ msandiford suggéré, mais cela ne fonctionne bien entendu que si vous êtes en mesure de fractionner Word les limites, ce qui le rend pas approprié comme une solution générale

Voici ma version, basée sur des idées empruntées à implémentation d'Apache . Ce n'est ni simple ni élégant, mais cela fonctionne et devrait être relativement efficace, sans étapes inutiles. En résumé, cela fonctionne comme suit: trouvez de manière répétée le prochain motif de recherche correspondant dans le texte et utilisez un StringBuilder pour accumuler les segments non appariés et les remplacements.

public static String replaceEach(String text, String[] searchList, String[] replacementList) {
    // TODO: throw new IllegalArgumentException() if any param doesn't make sense
    //validateParams(text, searchList, replacementList);

    SearchTracker tracker = new SearchTracker(text, searchList, replacementList);
    if (!tracker.hasNextMatch(0)) {
        return text;
    }

    StringBuilder buf = new StringBuilder(text.length() * 2);
    int start = 0;

    do {
        SearchTracker.MatchInfo matchInfo = tracker.matchInfo;
        int textIndex = matchInfo.textIndex;
        String pattern = matchInfo.pattern;
        String replacement = matchInfo.replacement;

        buf.append(text.substring(start, textIndex));
        buf.append(replacement);

        start = textIndex + pattern.length();
    } while (tracker.hasNextMatch(start));

    return buf.append(text.substring(start)).toString();
}

private static class SearchTracker {

    private final String text;

    private final Map<String, String> patternToReplacement = new HashMap<>();
    private final Set<String> pendingPatterns = new HashSet<>();

    private MatchInfo matchInfo = null;

    private static class MatchInfo {
        private final String pattern;
        private final String replacement;
        private final int textIndex;

        private MatchInfo(String pattern, String replacement, int textIndex) {
            this.pattern = pattern;
            this.replacement = replacement;
            this.textIndex = textIndex;
        }
    }

    private SearchTracker(String text, String[] searchList, String[] replacementList) {
        this.text = text;
        for (int i = 0; i < searchList.length; ++i) {
            String pattern = searchList[i];
            patternToReplacement.put(pattern, replacementList[i]);
            pendingPatterns.add(pattern);
        }
    }

    boolean hasNextMatch(int start) {
        int textIndex = -1;
        String nextPattern = null;

        for (String pattern : new ArrayList<>(pendingPatterns)) {
            int matchIndex = text.indexOf(pattern, start);
            if (matchIndex == -1) {
                pendingPatterns.remove(pattern);
            } else {
                if (textIndex == -1 || matchIndex < textIndex) {
                    textIndex = matchIndex;
                    nextPattern = pattern;
                }
            }
        }

        if (nextPattern != null) {
            matchInfo = new MatchInfo(nextPattern, patternToReplacement.get(nextPattern), textIndex);
            return true;
        }
        return false;
    }
}

Tests unitaires:

@Test
public void testSingleExact() {
    assertEquals("bar", StringUtils.replaceEach("foo", new String[]{"foo"}, new String[]{"bar"}));
}

@Test
public void testReplaceTwice() {
    assertEquals("barbar", StringUtils.replaceEach("foofoo", new String[]{"foo"}, new String[]{"bar"}));
}

@Test
public void testReplaceTwoPatterns() {
    assertEquals("barbaz", StringUtils.replaceEach("foobar",
            new String[]{"foo", "bar"},
            new String[]{"bar", "baz"}));
}

@Test
public void testReplaceNone() {
    assertEquals("foofoo", StringUtils.replaceEach("foofoo", new String[]{"x"}, new String[]{"bar"}));
}

@Test
public void testStory() {
    assertEquals("Once upon a foo, there was a bar and a baz, and another bar and a cat.",
            StringUtils.replaceEach("Once upon a baz, there was a foo and a bar, and another foo and a cat.",
                    new String[]{"foo", "bar", "baz"},
                    new String[]{"bar", "baz", "foo"})
    );
}
32
janos

Recherchez le premier mot à remplacer. Si c'est dans la chaîne, récidive 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 prochain mot à remplacer.

Une implémentation naïve pourrait ressembler à ceci

public static String replaceAll(String input, String[] search, String[] replace) {
  return replaceAll(input, search, replace, 0);
}

private static String replaceAll(String input, String[] search, String[] replace, int i) {
  if (i == search.length) {
    return input;
  }
  int j = input.indexOf(search[i]);
  if (j == -1) {
    return replaceAll(input, search, replace, i + 1);
  }
  return replaceAll(input.substring(0, j), search, replace, i + 1) +
         replace[i] +
         replaceAll(input.substring(j + search[i].length()), search, replace, i);
}

Exemple d'utilisation:

String input = "Once upon a baz, there was a foo and a bar.";
String[] search = new String[] { "foo", "bar", "baz" };
String[] replace = new String[] { "bar", "baz", "foo" };
System.out.println(replaceAll(input, search, replace));

Sortie:

Once upon a foo, there was a bar and a baz.

Une version moins naïve:

public static String replaceAll(String input, String[] search, String[] replace) {
  StringBuilder sb = new StringBuilder();
  replaceAll(sb, input, 0, input.length(), search, replace, 0);
  return sb.toString();
}

private static void replaceAll(StringBuilder sb, String input, int start, int end, String[] search, String[] replace, int i) {
  while (i < search.length && start < end) {
    int j = indexOf(input, search[i], start, end);
    if (j == -1) {
      i++;
    } else {
      replaceAll(sb, input, start, j, search, replace, i + 1);
      sb.append(replace[i]);
      start = j + search[i].length();
    }
  }
  sb.append(input, start, end);
}

Malheureusement, la String de Java n'a pas de méthode indexOf(String str, int fromIndex, int toIndex). J'ai omis la mise en œuvre de indexOf ici car je ne suis pas sûr que ce soit correct, mais vous pouvez le trouver sur ideone , avec quelques timings approximatifs des différentes solutions publiées ici.

21
mjolka

One-Liner in Java 8:

    story = Pattern
        .compile(String.format("(?<=%1$s)|(?=%1$s)", "foo|bar"))
        .splitAsStream(story)
        .map(w -> ImmutableMap.of("bar", "foo", "foo", "bar").getOrDefault(w, w))
        .collect(Collectors.joining());
12

Voici une possibilité de Java 8 flux qui pourrait être intéressante pour certains:

String Word1 = "bar";
String Word2 = "foo";

String story = "Once upon a time, there was a foo and a bar.";

// Map is from untranslated Word to translated Word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(Word1, Word2);
wordMap.put(Word2, Word1);

// Split on Word boundaries so we retain whitespace.
String translated = Arrays.stream(story.split("\\b"))
    .map(w -> wordMap.getOrDefault(w,  w))
    .collect(Collectors.joining());

System.out.println(translated);

Voici une approximation du même algorithme dans Java 7:

String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";

// Map is from untranslated Word to translated Word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(Word1, Word2);
wordMap.put(Word2, Word1);

// Split on Word boundaries so we retain whitespace.
StringBuilder translated = new StringBuilder();
for (String w : story.split("\\b"))
{
  String tw = wordMap.get(w);
  translated.append(tw != null ? tw : w);
}

System.out.println(translated);
11
msandiford

Si vous souhaitez remplacer des mots dans une phrase séparés par un espace, comme indiqué dans votre exemple, vous pouvez utiliser cet algorithme simple.

  1. Histoire partagée sur un espace blanc
  2. Remplacez chaque élément, si vous le remplacez, remplacez-le par barre et vice versa
  3. Rejoindre le tableau en une seule chaîne

Si la division de l'espace n'est pas acceptable, vous pouvez suivre cet algorithme alternatif. Vous devez d'abord utiliser la chaîne la plus longue. Si les chaînes sont foo et imbécile, vous devez utiliser imbécile en premier, puis foo.

  1. Split sur le mot foo
  2. Remplace bar par foo chaque élément du tableau
  3. Rejoindre ce tableau en ajoutant barre après chaque élément, sauf le dernier
6
fastcodejava

Voici une réponse moins compliquée en utilisant Map.

private static String replaceEach(String str,Map<String, String> map) {

         Object[] keys = map.keySet().toArray();
         for(int x = 0 ; x < keys.length ; x ++ ) {
             str = str.replace((String) keys[x],"%"+x);
         }

         for(int x = 0 ; x < keys.length ; x ++) {
             str = str.replace("%"+x,map.get(keys[x]));
         }
         return str;
     }

Et la méthode s'appelle

Map<String, String> replaceStr = new HashMap<>();
replaceStr.put("Raffy","awesome");
replaceStr.put("awesome","Raffy");
String replaced = replaceEach("Raffy is awesome, awesome awesome is Raffy Raffy", replaceStr);

La sortie est: génial c'est Raffy, Raffy Raffy est génial génial

5
WillingLearner

Cela fonctionne et est simple:

public String replaceBoth(String text, String token1, String token2) {            
    return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
    }

Vous l'utilisez comme ceci:

replaceBoth("Once upon a time, there was a foo and a bar.", "foo", "bar");

Remarque: cela compte sur les chaînes ne contenant pas le caractère \ufdd0, qui est un caractère en permanence réservé à l'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 en sécurité, vous pouvez utiliser:

public String replaceBoth(String text, String token1, String token2) {
    if (text.contains("\ufdd0") || token1.contains("\ufdd0") || token2.contains("\ufdd0")) throw new IllegalArgumentException("Invalid character.");
    return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
    }
3
MarcG

Vous pouvez atteindre votre objectif avec le bloc de code suivant:

String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = String.format(story.replace(Word1, "%1$s").replace(Word2, "%2$s"),
    Word2, Word1);

Il remplace les mots indépendamment de l'ordre. Vous pouvez étendre ce principe à une méthode utilitaire, telle que:

private static String replace(String source, String[] targets, String[] replacements) throws IllegalArgumentException {
    if (source == null) {
        throw new IllegalArgumentException("The parameter \"source\" cannot be null.");
    }

    if (targets == null || replacements == null) {
        throw new IllegalArgumentException("Neither parameters \"targets\" or \"replacements\" can be null.");
    }

    if (targets.length == 0 || targets.length != replacements.length) {
        throw new IllegalArgumentException("The parameters \"targets\" and \"replacements\" must have at least one item and have the same length.");
    }

    String outputMask = source;
    for (int i = 0; i < targets.length; i++) {
        outputMask = outputMask.replace(targets[i], "%" + (i + 1) + "$s");
    }

    return String.format(outputMask, (Object[])replacements);
}

Qui serait consommé comme:

String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = replace(story, new String[] { "bar", "foo" },
    new String[] { "foo", "bar" }));
3
Leonardo Braga

Si vous voulez pouvoir gérer plusieurs occurrences des chaînes de recherche à remplacer, vous pouvez le faire facilement en scindant la chaîne sur chaque terme de recherche, puis en la remplaçant. Voici un exemple:

String regex = Word1 + "|" + Word2;
String[] values = Pattern.compile(regex).split(story);

String result;
foreach subStr in values
{
   subStr = subStr.replace(Word1, Word2);
   subStr = subStr.replace(Word2, Word1);
   result += subStr;
}
3
ventsyv

Permutation 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 à une quelconque substitution, obtenez les index des occurrences des mots. Après cela, nous ne remplaçons que le mot trouvé dans ces index, et non toutes les occurrences. Cette solution utilise StringBuilder et ne produit pas d'intermédiaire String comme String.replace().

Une chose à noter: si les mots échangeables ont des longueurs différentes, après le premier remplacement, le deuxième index pourrait changer (si le premier mot survient avant le second) 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 longueurs différentes.

public static String swap(String src, String s1, String s2) {
    StringBuilder sb = new StringBuilder(src);
    int i1 = src.indexOf(s1);
    int i2 = src.indexOf(s2);

    sb.replace(i1, i1 + s1.length(), s2); // Replace s1 with s2
    // If s1 was before s2, idx2 might have changed after the replace
    if (i1 < i2)
        i2 += s2.length() - s1.length();
    sb.replace(i2, i2 + s2.length(), s1); // Replace s2 with s1

    return sb.toString();
}

Échange de nombre arbitraire d'occurrences

De manière analogue au 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, et pas seulement d'un int. Pour cela, nous allons utiliser la méthode d’utilité suivante:

public static List<Integer> occurrences(String src, String s) {
    List<Integer> list = new ArrayList<>();
    for (int idx = 0;;)
        if ((idx = src.indexOf(s, idx)) >= 0) {
            list.add(idx);
            idx += s.length();
        } else
            return list;
}

Et en utilisant cela, nous remplacerons les mots par l’autre en diminuant l’index (ce qui nécessitera peut-être d’alterner entre les 2 mots échangeables), de sorte que nous n’aurons même pas à corriger les index après un remplacement:

public static String swapAll(String src, String s1, String s2) {
    List<Integer> l1 = occurrences(src, s1), l2 = occurrences(src, s2);

    StringBuilder sb = new StringBuilder(src);

    // Replace occurrences by decreasing index, alternating between s1 and s2
    for (int i1 = l1.size() - 1, i2 = l2.size() - 1; i1 >= 0 || i2 >= 0;) {
        int idx1 = i1 < 0 ? -1 : l1.get(i1);
        int idx2 = i2 < 0 ? -1 : l2.get(i2);
        if (idx1 > idx2) { // Replace s1 with s2
            sb.replace(idx1, idx1 + s1.length(), s2);
            i1--;
        } else { // Replace s2 with s1
            sb.replace(idx2, idx2 + s2.length(), s1);
            i2--;
        }
    }

    return sb.toString();
}
3
icza

Il est facile d'écrire une méthode pour faire cela en utilisant String.regionMatches :

public static String simultaneousReplace(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    outer:
    for (int i = 0; i < subject.length(); i++) {
        for (int j = 0; j < pairs.length; j += 2) {
            String find = pairs[j];
            if (subject.regionMatches(i, find, 0, find.length())) {
                sb.append(pairs[j + 1]);
                i += find.length() - 1;
                continue outer;
            }
        }
        sb.append(subject.charAt(i));
    }
    return sb.toString();
}

Essai:

String s = "There are three cats and two dogs.";
s = simultaneousReplace(s,
    "cats", "dogs",
    "dogs", "budgies");
System.out.println(s);

Sortie:

Il y a trois chiens et deux perruches.

Cela n’est pas immédiatement évident, mais une fonction de ce type peut toujours dépendre de l’ordre dans lequel les remplacements sont spécifiés. Considérer:

String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplace(truth,
    "JavaScript", "Hamster",
    "Java", "Ham");
System.out.println(truth);

Sortie:

Java est à JavaScript comme Ham est à Hamster

Mais inversez les remplacements:

truth += " as " + simultaneousReplace(truth,
    "Java", "Ham",
    "JavaScript", "Hamster");

Sortie:

Java est à JavaScript comme Ham est à HamScript

Oops! :)

Par conséquent, il est parfois utile de rechercher la correspondance la plus longue (comme le fait la fonction strtr de PHP , par exemple). Cette version de la méthode fera cela:

public static String simultaneousReplace(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < subject.length(); i++) {
        int longestMatchIndex = -1;
        int longestMatchLength = -1;
        for (int j = 0; j < pairs.length; j += 2) {
            String find = pairs[j];
            if (subject.regionMatches(i, find, 0, find.length())) {
                if (find.length() > longestMatchLength) {
                    longestMatchIndex = j;
                    longestMatchLength = find.length();
                }
            }
        }
        if (longestMatchIndex >= 0) {
            sb.append(pairs[longestMatchIndex + 1]);
            i += longestMatchLength - 1;
        } else {
            sb.append(subject.charAt(i));
        }
    }
    return sb.toString();
}

Notez que les méthodes ci-dessus sont sensibles à la casse. Si vous avez besoin d'une version ne respectant pas la casse, il est facile de modifier ce qui précède car String.regionMatches peut prendre un paramètre ignoreCase.

2
Boann

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 foo-> bar, bar-foo, voici les résultats pour chaque itération:

  1. Il était une fois un foo et un bar. (contribution)
  2. Il était une fois un bar et un bar. (foo-> bar)
  3. Il était une fois, il y avait un foo et un foo. (bar-> toto, sortie)

Vous devez effectuer le remplacement en une itération sans revenir en arrière. Une solution de force brute est la suivante:

  1. Recherche l'entrée de position actuelle à la fin pour plusieurs chaînes de recherche jusqu'à ce qu'une correspondance soit trouvée
  2. Remplacer la chaîne de recherche correspondante par la chaîne de remplacement correspondante
  3. Définit la position actuelle sur le caractère suivant après la chaîne remplacée
  4. Répéter

Une fonction telle que String.indexOfAny(String[]) -> int[]{index, whichString} serait utile. Voici un exemple (pas le plus efficace):

private static String replaceEach(String str, String[] searchWords, String[] replaceWords) {
    String ret = "";
    while (str.length() > 0) {
        int i;
        for (i = 0; i < searchWords.length; i++) {
            String search = searchWords[i];
            String replace = replaceWords[i];
            if (str.startsWith(search)) {
                ret += replace;
                str = str.substring(search.length());
                break;
            }
        }
        if (i == searchWords.length) {
            ret += str.substring(0, 1);
            str = str.substring(1);
        }
    }
    return ret;
}

Quelques tests:

System.out.println(replaceEach(
    "Once upon a time, there was a foo and a bar.",
    new String[]{"foo", "bar"},
    new String[]{"bar", "foo"}
));
// Once upon a time, there was a bar and a foo.

System.out.println(replaceEach(
    "a p",
    new String[]{"a", "p"},
    new String[]{"Apple", "pear"}
));
// Apple pear

System.out.println(replaceEach(
    "ABCDE",
    new String[]{"A", "B", "C", "D", "E"},
    new String[]{"B", "C", "E", "E", "F"}
));
// BCEEF

System.out.println(replaceEach(
    "ABCDEF",
    new String[]{"ABCDEF", "ABC", "DEF"},
    new String[]{"XXXXXX", "YYY", "ZZZ"}
));
// XXXXXX
// note the order of search strings, longer strings should be placed first 
// in order to make the replacement greedy

Démo sur IDEONE
Démo sur IDEONE, code alternatif

2
Salman A

Si vous ne souhaitez pas de dépendances, vous pouvez simplement utiliser un tableau permettant uniquement un changement ponctuel. Ce n'est pas la solution la plus efficace, mais cela devrait fonctionner.

public String replace(String sentence, String[]... replace){
    String[] words = sentence.split("\\s+");
    int[] lock = new int[words.length];
    StringBuilder out = new StringBuilder();

    for (int i = 0; i < words.length; i++) {
        for(String[] r : replace){
            if(words[i].contains(r[0]) && lock[i] == 0){
                words[i] = words[i].replace(r[0], r[1]);
                lock[i] = 1;
            }
        }

        out.append((i < (words.length - 1) ? words[i] + " " : words[i]));
    }

    return out.toString();
}

Ensuite, cela fonctionnerait.

String story = "Once upon a time, there was a foo and a bar.";

String[] a = {"foo", "bar"};
String[] b = {"bar", "foo"};
String[] c = {"there", "Pocahontas"};
story = replace(story, a, b, c);

System.out.println(story); // Once upon a time, Pocahontas was a bar and a foo.

Eh bien, la réponse la plus courte est ...

String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
story = story.replace("foo", "@"+ Word1).replace("bar", Word2).replace("@" + Word2, Word1);
System.out.println(story);
1
Elvis Lima

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 ci-dessus SO answer. Créez deux tables d'index (disons que bar et foo n'apparaissent pas une fois dans votre chaîne) et vous pouvez utiliser ces tables sur les remplacer dans votre chaîne.

Maintenant, pour remplacer sur des emplacements d'index spécifiques, vous pouvez utiliser:

public static String replaceStringAt(String s, int pos, String c) {
   return s.substring(0,pos) + c + s.substring(pos+1);
}

Alors que pos est l’index de départ de vos chaînes (à partir des tables d’indexes citées plus haut). Supposons donc que vous ayez créé deux tables d’index pour chacune d’elles. Appelons-les indexBar et indexFoo.

Maintenant, en les remplaçant, vous pouvez simplement exécuter deux boucles, une pour chaque remplacement que vous souhaitez effectuer.

for(int i=0;i<indexBar.Count();i++)
replaceStringAt(originalString,indexBar[i],newString);

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 Google Maps ou autre.

Cela vous donnerait toujours le résultat que vous souhaitez 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é, il s'agit probablement de O (n carré), alors que n est la somme des occurrences des deux mots.

1
John Demetriou

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 remplacez le deuxième plus tard:

String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", "StringYouAreSureWillNeverOccur").replace("bar", "Word2").replace("StringYouAreSureWillNeverOccur", "Word1");

Notez que cela ne fonctionnera pas correctement si "StringYouAreSureWillNeverOccur" se produit.

1
Pokechu22
String Word1 = "bar";
String Word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."

Peu astucieux, mais vous devez faire quelques vérifications supplémentaires.

1.convertir la chaîne en tableau de caractères

   String temp[] = story.split(" ");//assume there is only spaces.

2. boucle sur temp et remplacez foo par bar et bar par foo car il n'y a aucune chance d'obtenir une chaîne remplaçable à nouveau.

1
Tony Stark

Pensez à utiliser StringBuilder

Puis stockez l'index où chaque chaîne doit commencer. Si vous utilisez un caractère de lieu 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 la chaîne à la position de départ.

String firstString = "???";
String secondString  = "???"

StringBuilder story = new StringBuilder("One upon a time, there was a " 
    + firstString
    + " and a "
    + secondString);

int  firstWord = 30;
int  secondWord = firstWord + firstString.length() + 7;

story.replace(firstWord, firstWord + firstString.length(), userStringOne);
story.replace(secondWord, secondWord + secondString.length(), userStringTwo);

firstString = userStringOne;
secondString = userStringTwo;

return story;
1
Imheroldman

Voici ma version, qui est basée sur Word:

class TextReplace
{

    public static void replaceAll (String text, String [] lookup,
                                   String [] replacement, String delimiter)
    {

        String [] words = text.split(delimiter);

        for (int i = 0; i < words.length; i++)
        {

            int j = find(lookup, words[i]);

            if (j >= 0) words[i] = replacement[j];

        }

        text = StringUtils.join(words, delimiter);

    }

    public static  int find (String [] array, String key)
    {

        for (int i = 0; i < array.length; i++)
            if (array[i].equals(key))
                return i;

        return (-1);

    }

}
1
Khaled.K

Ce que je ne peux que partager, c'est ma propre méthode.

Vous pouvez utiliser un String temp = "<?>"; Ou String.Format(); temporaire

Ceci est mon exemple de code créé dans une application console via c # "Idée seule, pas de réponse exacte".

static void Main(string[] args)
    {
        String[] Word1 = {"foo", "Once"};
        String[] Word2 = {"bar", "time"};
        String story = "Once upon a time, there was a foo and a bar.";

        story = Switcher(story,Word1,Word2);
        Console.WriteLine(story);
        Console.Read();
    }
    // Using a temporary string.
    static string Switcher(string text, string[] target, string[] value)
    {
        string temp = "<?>";
        if (target.Length == value.Length)
        {
            for (int i = 0; i < target.Length; i++)
            {
                text = text.Replace(target[i], temp);
                text = text.Replace(value[i], target[i]);
                text = text.Replace(temp, value[i]);
            }
        }
        return text;
    }

Ou vous pouvez aussi utiliser la String.Format();

static string Switcher(string text, string[] target, string[] value)
        {
            if (target.Length == value.Length)
            {
                for (int i = 0; i < target.Length; i++)
                {
                    text = text.Replace(target[i], "{0}").Replace(value[i], "{1}");
                    text = String.Format(text, value[i], target[i]);
                }
            }
            return text;
        }

Sortie: time upon a Once, there was a bar and a foo.

1
Leonel Sarmiento