web-dev-qa-db-ja.com

一方が他方を置き換えないように2つの文字列を置き換えるにはどうすればよいですか?

次のコードがあるとしましょう:

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

このコードの実行後、storyの値は"Once upon a time, there was a foo and a foo."になります

逆の順序で置き換えた場合も同様の問題が発生します。

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

storyの値は"Once upon a time, there was a bar and a bar."になります

私の目標は、story"Once upon a time, there was a bar and a foo."に変えることです。

159
Pikamander2

replaceEach() メソッドを Apache Commons StringUtils から使用します。

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

中間値を使用します(文にはまだありません)。

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

批判への対応として:zq515sqdqs5d5sq1dqs4d1q5dqqé "&é5d4sqjshsjddjhodfqsqc、nvùq^ µù; d&€sdq:d:;)àçàçlalaのようなユーザーがこれを入力するかどうかについては議論しませんが、ユーザーがソースコードを知っているかどうかを知る唯一の方法は、その時点で他のレベルの心配事があることです。

はい、多分派手な正規表現の方法があります。私は、私にも分からないことがわかっている読みやすいものを好みます。

また、 コメントの@David Conrad :による優れたアドバイスを繰り返します。

可能性が低いと思われる文字列を(愚かに)巧妙に使用しないでください。 Unicode Private Use Areaの文字、U + E000..U + F8FFを使用します。これらの文字は、入力に正当に含まれてはならないため(アプリケーション内でアプリケーション固有の意味しか持たないため)最初に削除し、置換するときにプレースホルダーとして使用します。

87
Jeroen Vannevel

Matcher#appendReplacement および 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());
昔々、バーとフーがありました。
33
arshajii

これは簡単な問題ではありません。また、検索置換パラメータが多いほど、複雑になります。 severalい上品で効率的で無駄の多いパレットに散らばったいくつかのオプションがあります。

  • つかいます StringUtils.replaceEach@ AlanHay としてApache Commonsから。これは、プロジェクトに新しい依存関係を自由に追加できる場合に適したオプションです。あなたは幸運になるかもしれません:依存関係はすでにプロジェクトに含まれているかもしれません

  • @ Jeroen のように一時的なプレースホルダーを使用し、2つの手順で置換を実行します。

    1. すべての検索パターンを元のテキストに存在しない一意のタグに置き換えます
    2. プレースホルダーを実際のターゲット置換に置き換えます

    これは、いくつかの理由により、優れたアプローチではありません。最初のステップで使用されるタグが本当に一意であることを確認する必要があります。実際に必要なものよりも多くの文字列置換操作を実行します

  • すべてのパターンから正規表現を構築し、 MatcherおよびStringBuffer で提案されているように、 @ arshajii で提案されているメソッドを使用します。これはひどいことではありませんが、それほど素晴らしいことでもありません。正規表現の構築は一種のハックであり、StringBufferを支持してしばらく前に流行したStringBuilderを含んでいます。

  • @ mjolka によって提案された再帰的ソリューションを使用します。一致するパターンで文字列を分割し、残りのセグメントで再帰します。これは素晴らしいソリューションで、コンパクトで非常にエレガントです。その弱点は、潜在的に多くの部分文字列および連結操作であり、すべての再帰的ソリューションに適用されるスタックサイズの制限です。

  • テキストを単語に分割し、Java 8ストリームを使用して @ msandiford が示唆するように置換をエレガントに実行しますが、もちろん、Wordで分割しても問題ない場合にのみ機能します境界線。一般的なソリューションとしては適切ではありません。

Apacheの実装 から借用したアイデアに基づく、私のバージョンです。それは単純でもエレガントでもありませんが、機能し、不必要な手順なしで比較的効率的でなければなりません。簡単に言うと、次のように機能します。テキスト内で次の一致する検索パターンを繰り返し検索し、StringBuilderを使用して、一致しないセグメントと置換を蓄積します。

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

単体テスト:

@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

置換する最初のWordを検索します。文字列内にある場合は、出現する前の文字列の部分、および出現した後の文字列の部分で再帰します。

それ以外の場合は、置換される次のWordに進みます。

素朴な実装は次のようになります

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

サンプル使用法:

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

出力:

_Once upon a foo, there was a bar and a baz.
_

それほど単純ではないバージョン:

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

残念ながら、JavaのStringにはindexOf(String str, int fromIndex, int toIndex)メソッドがありません。 indexOfの実装をここでは省略しましたが、正しいかどうかはわかりませんが、 ideone で見つけることができます。

21
mjolka

Java 8:の1行

    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());
  • 見回す正規表現(?<=?=): http://www.regular-expressions.info/lookaround.html
  • 単語に特殊な正規表現文字を含めることができる場合は、 Pattern.quote を使用してエスケープします。
  • 簡潔にするためにguava ImmutableMapを使用していますが、他のマップでも同様に機能します。
12

ここにJava 8ストリームの可能性があります。

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

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

あなたの例に示されているように空白で区切られた文の単語を置き換えたい場合は、この単純なアルゴリズムを使用できます。

  1. ホワイトスペースでのストーリーの分割
  2. Fooがbarに置き換え、その逆の場合は各要素を置き換えます
  3. 配列を1つの文字列に戻します

空間での分割が受け入れられない場合、この代替アルゴリズムに従うことができます。長い文字列を最初に使用する必要があります。文字列がfooとfoolの場合、最初にfoolを使用し、次にfooを使用する必要があります。

  1. Word fooで分割
  2. 配列の各要素をbarでfooに置き換えます
  3. 最後の要素を除く各要素の後にバーを追加して、その配列を結合します
6
fastcodejava

これは、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;
     }

そして、メソッドが呼び出されます

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

出力は:awesome is Raffy、Raffy Raffy is awesome awesome

5
WillingLearner

これは機能し、簡単です。

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

次のように使用します。

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

注:これは、文字\ufdd0を含まない文字列にカウントされます。これは、文字永久 Unicodeによる内部使用のために予約されています( http://www.unicode.org/faqを参照してください/private_use.html ):

私はそれが必要だとは思わないが、あなたが絶対に安全になりたいなら、あなたは使うことができる:

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

次のコードブロックで目標を達成できます。

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

順序に関係なく単語を置き換えます。この原則は、次のようなユーティリティメソッドに拡張できます。

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

次のように消費されます:

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

置換される検索文字列の複数の出現を処理できるようにしたい場合は、各検索語で文字列を分割してから置換することで簡単に実行できます。以下に例を示します。

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

1回のみのスワップ

入力にスワップ可能な文字列がそれぞれ1つしか存在しない場合は、次の操作を実行できます。

置換に進む前に、単語の出現のインデックスを取得します。その後、これらのインデックスで見つかったWordのみを置換し、すべての出現を置換するわけではありません。このソリューションはStringBuilderを使用し、String.replace()のような中間のStringsを生成しません。

留意すべき点が1つあります。スワップ可能な単語の長さが異なる場合、最初の置換後、2番目のインデックスが2番目の長さの差で正確に変化する可能性があります。したがって、2番目のインデックスを揃えることにより、長さの異なる単語を交換する場合でもこれが機能することが保証されます。

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

発生の任意の数のスワップ

前のケースと同様に、最初に単語のインデックス(オカレンス)を収集しますが、この場合は、1つのintではなく、各Wordの整数のリストを収集します。このために、次のユーティリティメソッドを使用します。

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

そして、これを使用して、インデックスを減らすことで単語を他の単語に置き換えます(2つのスワップ可能な単語を交互に切り替える必要があります)。これにより、置換後にインデックスを修正する必要がなくなります。

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

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

テスト:

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

出力:

3匹の犬と2匹のセキセイインコがいます。

すぐにはわかりませんが、このような関数は、置換が指定された順序に依存する可能性があります。考慮してください:

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

出力:

JavaはJavaScriptに対して、Hamはハムスターに対して

ただし、置換を逆にします。

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

出力:

JavaがJavaScriptに対して、HamがHamScriptに対して

おっと! :)

そのため、longest一致(PHPの strtr 関数のように) 、 例えば)。このバージョンのメソッドはそれを行います:

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

上記のメソッドでは大文字と小文字が区別されることに注意してください。大文字と小文字を区別しないバージョンが必要な場合は、String.regionMatchesignoreCaseパラメーターを取ることができるため、上記を簡単に変更できます。

2
Boann

入力に対して複数の検索置換操作を実行しています。これにより、置換文字列に検索文字列が含まれている場合、望ましくない結果が生成されます。 foo-> bar、bar-fooの例を考えてみましょう。各反復の結果は次のとおりです。

  1. 昔々、fooとbarがありました。 (入力)
  2. むかしむかし、バーとバーがありました。 (foo-> bar)
  3. 昔々、fooとfooがありました。 (bar-> foo、出力)

戻ることなく、1回の反復で置換を実行する必要があります。ブルートフォースソリューションは次のとおりです。

  1. 一致が見つかるまで、入力を現在の位置から検索して複数の検索文字列を終了します
  2. 一致した検索文字列を対応する置換文字列で置き換えます
  3. 現在の位置を、置換された文字列の次の文字に設定します
  4. 繰り返す

String.indexOfAny(String[]) -> int[]{index, whichString}などの関数が便利です。次に例を示します(最も効率的な例ではありません)。

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

いくつかのテスト:

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

IDEONEのデモ
IDEONEのデモ、代替コード

2
Salman A

依存関係が必要ない場合は、1回限りの変更のみを許可する配列を使用できます。これは最も効率的なソリューションではありませんが、機能するはずです。

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

次に、それは動作します。

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.

まあ、短い答えは...

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

見つかった答え here を使用すると、置換したい文字列のすべての出現を見つけることができます。

したがって、たとえば上記のコードを実行しますSO answer。インデックスの2つのテーブルを作成します(たとえば、barとfooは文字列に1回だけ表示されません)。あなたの文字列でそれらを置き換えます。

特定のインデックスの場所を置き換えるために、次を使用できます。

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

一方、posは文字列が始まるインデックスです(上で引用したインデックステーブルから)。そこで、それぞれに2つのインデックステーブルを作成したとしましょう。それらをindexBarおよびindexFooと呼びましょう。

これで、それらを置換する際に、作成したい置換ごとに1つずつ、2つのループを実行できます。

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

同様に、indexFooの別のループ。

これはここでの他の回答ほど効率的ではないかもしれませんが、マップや他のものよりも理解するのは簡単です。

これにより、常に希望する結果が得られ、各文字列が複数回出現する可能性があります。各オカレンスのインデックスを保存している限り。

また、この答えには再帰も外部依存関係も必要ありません。複雑さに関しては、O(nの2乗)が適切です。一方、nは両方の単語の出現の合計です。

1
John Demetriou

文字列のどこにも表示されないことが確実なWordにいつでも置き換えることができ、その後2回目の置き換えを行います。

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

"StringYouAreSureWillNeverOccur"が発生した場合、これは正しく機能しないことに注意してください。

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

少し注意が必要ですが、さらにチェックする必要があります。

1.文字列を文字配列に変換する

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

2. tempでループし、foobarに、barfooに置き換えます。置換可能な文字列を再度取得する可能性はありません。

1
Tony Stark

StringBuilder の使用を検討してください

次に、各文字列の開始位置にインデックスを保存します。各位置でプレースホルダー文字を使用する場合は、それを削除して、ユーザー文字列を挿入します。その後、文字列の長さを開始位置に追加して、終了位置をマップできます。

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

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

共有できるのは自分の方法だけです。

一時的な_String temp = "<?>";_またはString.Format();を使用できます

これは、 c#-"Idea Only、Not Exact Answer"を介してコンソールアプリケーションで作成された私のサンプルコードです。

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

または、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;
        }
_

出力:_time upon a Once, there was a bar and a foo._

1