Comment puis-je remplacer deux cordes de manière à ce que l'une ne finisse pas par remplacer 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);

après l'exécution de ce code, 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?

158
demandé sur icza 2014-11-07 02:33:46

25 réponses

utiliser la replaceEach() méthode de Apache communs StringUtils :

StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})
86
répondu Alan Hay 2014-11-09 01:49:33

Vous utilisez une valeur intermédiaire (qui n'est pas encore présente dans la phrase).

story = story.replace("foo", "lala");
story = story.replace("bar", "foo");
story = story.replace("lala", "bar");

comme une réponse à la critique: si vous utilisez une chaîne assez grande peu commune comme zq515sqdqs5d5sq1dqs4d1q5dqqqé"&é5d4sqjshsjddjhodfqsqc, nvùq^µù;d&€sdq: d: ;)àçàçlala et utilisez cela, il est peu probable au point où je ne vais même pas débattre que un utilisateur n'entrera jamais ceci. La seule façon de savoir si un utilisateur est en connaissant le code source et à ce point vous êtes avec un tout autre niveau de soucis.

oui, il y a peut-être des chemins de regex Fantaisie. Je préfère quelque chose de lisible qui, je le sais, ne m'échappera pas non plus.

réitérant également l'excellent conseil donné par @David Conrad dans les commentaires :

N'utilisez pas une corde astucieusement (stupidement) choisie pour être improbable. Utilisez les caractères de la zone D'utilisation privée Unicode, U+E000..U+F8FF. Supprimez d'abord ces caractères, puisqu'ils ne devraient pas être légitimement dans l'entrée (ils ont seulement une signification spécifique à l'application dans une certaine application), puis utilisez-les comme des espaces réservés lors du remplacement.

87
répondu Jeroen Vannevel 2017-05-23 10:30:04

vous pouvez essayer quelque chose comme ceci, 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());
Once upon a time, there was a bar and a foo.
33
répondu arshajii 2014-11-06 23:59:28

Ce n'est pas un problème facile. Et plus vous avez de paramètres de recherche-remplacement, plus ça devient compliqué. Vous avez plusieurs options, éparpillées sur la palette de laid-élégant, efficace-gaspillage:

  • utilisez 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 être inclus déjà dans votre projet

  • utilisez un emplacement temporaire comme @Jeroen suggéré, et effectuer le remplacement en 2 étapes:

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

    Ce n'est pas une bonne approche, pour plusieurs raisons: il doit s'assurer que les étiquettes utilisées dans la première étape sont vraiment uniques; il effectue plus d'opérations de remplacement de chaîne que vraiment nécessaire

  • construisez un regex à partir de tous les motifs et utilisez la méthode avec Matcher et StringBuffer comme suggéré par @arshajii . Ce n'est pas terrible, mais ce n'est pas si grand non plus, car la construction du regex est une sorte de hackish, et il s'agit de StringBuffer qui il y a un certain temps, il a été démodé en faveur de StringBuilder .

  • utilisez une solution récursive proposée par @mjolka , en séparant la chaîne aux motifs appariés, et en revenant sur les segments restants. C'est une solution fine, compacte et très élégante. Sa faiblesse réside dans le fait que les opérations de sous-traitance et de concaténation peuvent être nombreuses et dans les limites de taille des cheminées qui s'appliquent à toutes les solutions récursives

    1519160920"
  • Diviser le texte en mots et l'utilisation de Java 8 flux pour effectuer les remplacements élégante @msandiford , mais bien sûr, cela ne fonctionne que si vous êtes ok avec le fractionnement aux limites des mots, qui en fait ne conviennent pas comme une solution générale

voici ma version, basée sur des idées empruntées à Apache's implementation . Ce n'est ni simple ni élégant, mais il fonctionne, et devrait être relativement efficace, sans étapes inutiles. En un mot, cela fonctionne comme ceci: à plusieurs reprises trouver le prochain modèle de recherche correspondant dans le texte, et utiliser 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;
    }
}

Essais 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
répondu janos 2017-05-23 10:30:04

rechercher le premier mot à remplacer. Si c'est dans la chaîne, de manière récursive sur la partie de la chaîne avant l'événement, et sur la partie de la chaîne après l'événement.

sinon, continuer avec le mot suivant à 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, le String de Java n'a pas de méthode indexOf(String str, int fromIndex, int toIndex) . J'ai omis l'implémentation de indexOf ici car je ne suis pas certain que ce soit correct, mais il peut être trouvé sur ideone , ainsi que quelques horaires rugueux de diverses solutions posté ici.

21
répondu mjolka 2014-11-12 02:07:16

One-liner en 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
répondu Vitalii Fedorenko 2014-11-08 21:28:31

Voici une possibilité Java 8 streams 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 en 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
répondu msandiford 2014-11-07 00:07:50

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

  1. Split histoire sur l'espace blanc
  2. remplacer chaque élément, si foo le remplacer à bar et vice varsa
  3. Rejoindre le tableau en une chaîne

si le fractionnement dans l'espace n'est pas acceptable, on peut suivre cet autre algorithme. ​Vous besoin d'utiliser la plus longue chaîne. Si les cordes sont foo et fool, vous devez utiliser fool d'abord et ensuite foo.

  1. Split sur le mot toto
  2. remplacer bar par foo chaque élément du tableau
  3. Rejoindre ce tableau en ajoutant bar après chaque élément, sauf le dernier,
6
répondu fastcodejava 2014-11-18 06:15:59

Voici une réponse moins compliquée à L'aide de la carte.

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 est appelée

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

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

5
répondu WillingLearner 2014-11-10 03:07:51

si vous voulez être capable de gérer plusieurs occurrences des chaînes de recherche à remplacer, vous pouvez le faire facilement en séparant 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
répondu ventsyv 2014-11-07 00:39:23

vous pouvez accomplir votre but 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);

elle remplace les mots indépendamment de l'ordre. Vous pouvez étendre ce principe dans une méthode d'utilité, comme:

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
répondu Leonardo Braga 2014-11-12 05:04:22

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

Note: cela compte sur les chaînes ne contenant pas le caractère \ufdd0 , qui est un caractère permanent réservé à 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:

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
répondu MarcG 2014-11-14 02:00:35

Échange Une Seule Occurrence De

S'il n'y a qu'une occurrence de chacune des chaînes swapables dans l'entrée, vous pouvez faire ce qui suit:

Avant de procéder à tout remplacer, obtenir les indices des occurrences des mots. Après cela, nous remplaçons seulement le mot trouvé à ces index, et pas tous les événements. Cette solution utilise StringBuilder et ne produit pas de String intermédiaire comme String.replace() .

une chose à noter: si les mots swapables ont des longueurs différentes, après le premier remplacer le deuxième index pourrait changer (si le premier mot se produit avant le 2nd) exactement avec la différence des 2 longueurs. Ainsi, l'alignement du deuxième index assurera que cela fonctionne même si nous échangeons des mots avec des 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();
}

Permutation Arbitraire Nombre d'Occurrences

comme dans le cas précédent, nous collecterons d'abord les indices (occurrences) des mots, mais dans ce cas, il sera d'une liste d'entiers, pour chaque mot, et pas seulement un int . Pour cela, nous utiliserons 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 ceci, nous allons remplacer les mots par l'autre en diminuant l'index (qui pourrait nécessiter d'alterner entre les 2 mots swapables) de sorte que nous n'aurons même pas à corriger les indices après un remplacer:

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
répondu icza 2015-10-20 07:29:00

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

test:

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.

ce n'est pas évident à première vue, mais une fonction comme celle-ci peut encore dépendre de l'ordre dans lequel les remplacements sont spécifiés. Prendre en considération:

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

sortie:

Java, JavaScript comme le Jambon est pour Hamster

mais inversez les remplacements:

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

sortie:

Java, JavaScript comme le Jambon est à HamScript

Oops! :)

par conséquent, il est parfois utile de s'assurer de chercher le la plus longue correspondance (comme 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();
}

noter 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 un paramètre ignoreCase .

2
répondu Boann 2014-11-09 08:05:48

Si vous ne voulez pas de dépendances, vous pouvez simplement utiliser un tableau qui permet un changement d'heure seulement. Ce n'est pas la solution la plus efficace, mais il 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();
}

alors, ça devrait marcher.

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.
2
répondu Pier-Alexandre Bouchard 2014-11-10 18:12:19

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. Considérons l'exemple foo->bar, bar-foo, voici les résultats pour chaque itération:

  1. il était une fois un foo et un bar. (entrée)
  2. il était une fois un bar et un bar. (foo->bar)
  3. il était une fois un foo et un foo. (bar->foo, sortie)

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

  1. rechercher l'entrée de la position courante à la fin pour plusieurs chaînes de recherche jusqu'à ce qu'une correspondance soit trouvée
  2. remplacer la chaîne de recherche appariée par la chaîne de remplacement correspondante
  3. placer la position actuelle au 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 essais:

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, de l'alternance de code

2
répondu Salman A 2014-11-12 17:23:19

Vous pouvez toujours le remplacer par un mot, vous êtes sûr apparaît nulle part ailleurs dans la chaîne, puis la deuxième remplacer 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
répondu Pokechu22 2014-11-06 23:38:34

envisager d'utiliser StringBuilder

stocke alors l'index où chaque chaîne doit commencer. Si vous utilisez un caractère de support de place à chaque position, puis le supprimer, et insérer la chaîne de caractères 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
répondu Imheroldman 2014-11-11 23:44:27

ce que je ne peux partager est ma propre méthode.

vous pouvez utiliser un String temp = "<?>"; ou String.Format(); temporaire 151970920"

C'est mon exemple de code créé dans l'application console via - " idée seulement, Pas 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 également utiliser le 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
répondu Leonel Sarmiento 2014-11-12 07:20:14

voici ma version, qui est basée sur les mots:

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
répondu Khaled.K 2014-11-12 14:04:49
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."

petit chemin délicat, mais vous avez besoin de faire plus de contrôles.

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

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

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

1
répondu Tony Stark 2014-11-13 12:48:48

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
répondu Elvis Lima 2014-11-20 16:05:30

en utilisant la réponse trouvée ici vous pouvez trouver toutes les occurrences des chaînes que vous souhaitez remplacer par.

donc par exemple vous exécutez le code dans la réponse 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:

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 où vos chaînes commencent (à partir des tables d'index i citées ci-dessus). Donc, disons que vous avez créé deux tables d'index pour chacun. Appelons-les indexBar et indexFoo .

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

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

de même une autre boucle pour indexFoo .

Cela peut ne pas être aussi efficace que d'autres réponses ici, mais c'est plus simple à comprendre que des Cartes ou d'autres choses.

cela vous donnera toujours le résultat que vous souhaitez et pour plusieurs occurrences possibles de chaque chaîne. Aussi longtemps que vous stockez l'index de chaque occurrence.

cette réponse n'a pas besoin non plus de récursion ni de dépendances externes. En ce qui concerne la complexité, il est probable Qu'elle est O(N au carré), tandis que n est la somme des occurences des deux mots.

1
répondu John Demetriou 2017-05-23 12:18:33

j'ai développé ce code va résoudre le problème:

public static String change(String s,String s1, String s2) {
   int length = s.length();
   int x1 = s1.length();
   int x2 = s2.length();
   int x12 = s.indexOf(s1);
   int x22 = s.indexOf(s2);
   String s3=s.substring(0, x12);
   String s4 =s.substring(x12+3, x22);
   s=s3+s2+s4+s1;
   return s;
}

dans l'usage principal change(story,word2,word1).

-1
répondu Onur 2014-11-07 00:04:09
String word1 = "bar";
String word2 = "foo";

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

story = story.replace("foo", "<foo />");
story = story.replace("bar", "<bar />");

story = story.replace("<foo />", word1);
story = story.replace("<bar />", word2);
-1
répondu Amir Saniyan 2014-11-12 08:47:15