Angenommen, ich habe den folgenden Code:
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);
Nachdem dieser Code ausgeführt wird , wird der Wert von story
seinem"Once upon a time, there was a foo and a foo."
Ein ähnliches Problem tritt auf, wenn ich sie in umgekehrter Reihenfolge ersetzt habe:
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);
Der Wert von story
wird sein"Once upon a time, there was a bar and a bar."
Mein Ziel ist es, sich story
in "Once upon a time, there was a bar and a foo."
Wie kann ich das erreichen?
swap(String s1, String s2, String s3)
, die alle Vorkommen vons2
mit vertauschts3
und umgekehrt.Antworten:
Verwenden Sie die
replaceEach()
Methode von Apache Commons StringUtils :quelle
null
ist, wenn es bestanden wird.Sie verwenden einen Zwischenwert (der im Satz noch nicht vorhanden ist).
Als Antwort auf Kritik: Wenn Sie eine ausreichend große ungewöhnliche Zeichenfolge wie zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddjhodfqsqc, nvùq ^ µù; d & € sdq: d :); àçàçlala verwenden und Gebrauch , dass es auf den Punkt unwahrscheinlich ist , wo ich es nicht einmal debattieren dass ein Benutzer dies jemals eingeben wird. Der einzige Weg zu wissen, ob ein Benutzer dies tun wird, besteht darin, den Quellcode zu kennen, und an diesem Punkt haben Sie eine ganz andere Ebene von Sorgen.
Ja, vielleicht gibt es ausgefallene Regex-Möglichkeiten. Ich bevorzuge etwas Lesbares, von dem ich weiß, dass es auch bei mir nicht ausbricht.
Wiederholen Sie auch den ausgezeichneten Rat von @David Conrad in den Kommentaren :
quelle
Sie können so etwas ausprobieren, indem Sie
Matcher#appendReplacement
undMatcher#appendTail
:quelle
foo
,bar
undstory
alle haben unbekannte Werte?"foo"
und die"bar"
Ersetzungszeichenfolgen im Wesentlichen fest codiert, wie sie das OP in seinem Code hatte, aber der gleiche Ansatz würde gut funktionieren, selbst wenn diese Werte nicht bekannt sind (Sie müsstenif
/else if
anstelle von aswitch
innerhalb der verwendenwhile
-Schleife).Pattern.quote
würde sich als nützlich erweisen, oder\Q
und\E
.(foo)|(bar)
und dann dagegen zu prüfenm.group(1) != null
, um zu vermeiden, dass die passenden Wörter wiederholt werden.Dies ist kein einfaches Problem. Und je mehr Suchersetzungsparameter Sie haben, desto schwieriger wird es. Sie haben mehrere Möglichkeiten, die auf der Palette hässlich-elegant, effizient-verschwenderisch verteilt sind:
Verwendung
StringUtils.replaceEach
von Apache Commons wie von @AlanHay empfohlen. Dies ist eine gute Option, wenn Sie Ihrem Projekt neue Abhängigkeiten hinzufügen können. Sie könnten Glück haben: Die Abhängigkeit ist möglicherweise bereits in Ihrem Projekt enthaltenVerwenden Sie einen temporären Platzhalter, wie von @Jeroen vorgeschlagen, und führen Sie den Austausch in zwei Schritten durch:
Dies ist aus mehreren Gründen kein guter Ansatz: Er muss sicherstellen, dass die im ersten Schritt verwendeten Tags wirklich eindeutig sind. Es führt mehr String-Ersetzungsoperationen durch als wirklich notwendig
Erstellen Sie einen regulären Ausdruck aus allen Mustern und verwenden Sie die Methode mit
Matcher
undStringBuffer
wie von @arshajii vorgeschlagen . Das ist nicht schrecklich, aber auch nicht so toll, da der Aufbau des regulären Ausdrucks irgendwie hackisch ist und beinhaltet,StringBuffer
was vor einiger Zeit zugunsten von aus der Mode gekommen istStringBuilder
.Verwenden Sie eine von @mjolka vorgeschlagene rekursive Lösung , indem Sie die Zeichenfolge an den übereinstimmenden Mustern aufteilen und die verbleibenden Segmente rekursiv ausführen . Dies ist eine gute Lösung, kompakt und sehr elegant. Seine Schwäche sind die potenziell vielen Teilzeichenfolgen- und Verkettungsvorgänge sowie die Stapelgrößenbeschränkungen, die für alle rekursiven Lösungen gelten
Teilen Sie den Text in Wörter auf und verwenden Sie Java 8-Streams, um die Ersetzungen elegant durchzuführen , wie von @msandiford vorgeschlagen. Dies funktioniert natürlich nur, wenn Sie mit dem Teilen an Wortgrenzen einverstanden sind, was es nicht als allgemeine Lösung geeignet macht
Hier ist meine Version, basierend auf Ideen, die aus der Implementierung von Apache entlehnt wurden . Es ist weder einfach noch elegant, aber es funktioniert und sollte relativ effizient sein, ohne unnötige Schritte. Kurz gesagt, es funktioniert folgendermaßen: Suchen Sie wiederholt das nächste übereinstimmende Suchmuster im Text und verwenden Sie a
StringBuilder
, um die nicht übereinstimmenden Segmente und die Ersetzungen zu akkumulieren.Unit Tests:
quelle
Suchen Sie nach dem ersten Wort, das ersetzt werden soll. Wenn es sich in der Zeichenfolge befindet, wiederholen Sie den Teil der Zeichenfolge vor dem Auftreten und den Teil der Zeichenfolge nach dem Auftreten.
Fahren Sie andernfalls mit dem nächsten zu ersetzenden Wort fort.
Eine naive Implementierung könnte so aussehen
Beispielnutzung:
Ausgabe:
Eine weniger naive Version:
Leider hat Java
String
keineindexOf(String str, int fromIndex, int toIndex)
Methode. Ich habe die Implementierung vonindexOf
hier weggelassen , da ich nicht sicher bin, ob sie korrekt ist, aber sie kann auf ideone gefunden werden , zusammen mit einigen groben Zeitplänen verschiedener hier veröffentlichter Lösungen.quelle
Einzeiler in Java 8:
?<=
,?=
): http://www.regular-expressions.info/lookaround.htmlquelle
Hier ist eine Möglichkeit für Java 8-Streams, die für einige interessant sein könnte:
Hier ist eine Annäherung an denselben Algorithmus in Java 7:
quelle
Wenn Sie Wörter in einem Satz ersetzen möchten, die wie in Ihrem Beispiel durch Leerzeichen getrennt sind, können Sie diesen einfachen Algorithmus verwenden.
Wenn das Aufteilen auf Platz nicht akzeptabel ist, kann man diesem alternativen Algorithmus folgen. Sie müssen zuerst die längere Zeichenfolge verwenden. Wenn die Zeichenfolgen foo und dumm sind, müssen Sie zuerst dumm und dann foo verwenden.
quelle
Hier ist eine weniger komplizierte Antwort mit Map.
Und Methode heißt
Die Ausgabe ist: fantastisch ist Raffy, Raffy Raffy ist fantastisch fantastisch
quelle
replaced.replaceAll("Raffy", "Barney");
danach wird es für sie legen ... wait machen; Dary !!!Wenn Sie in der Lage sein möchten, mehrere Vorkommen der zu ersetzenden Suchzeichenfolgen zu verarbeiten, können Sie dies einfach tun, indem Sie die Zeichenfolge auf jeden Suchbegriff aufteilen und dann ersetzen. Hier ist ein Beispiel:
quelle
Sie können Ihr Ziel mit dem folgenden Codeblock erreichen:
Es ersetzt die Wörter unabhängig von der Reihenfolge. Sie können dieses Prinzip auf eine Utility-Methode erweitern, z.
Welches würde verbraucht werden als:
quelle
Das funktioniert und ist einfach:
Sie verwenden es so:
Hinweis: Dies gilt für Zeichenfolgen, die kein Zeichen enthalten. Dieses Zeichen
\ufdd0
ist permanent für die interne Verwendung durch Unicode reserviert (siehe http://www.unicode.org/faq/private_use.html) ):Ich denke nicht, dass es notwendig ist, aber wenn Sie absolut sicher sein wollen, können Sie verwenden:
quelle
Nur ein Ereignis tauschen
Wenn die austauschbaren Zeichenfolgen in der Eingabe nur einmal vorkommen, können Sie Folgendes tun:
Bevor Sie mit dem Ersetzen fortfahren, ermitteln Sie die Indizes für das Vorkommen der Wörter. Danach ersetzen wir nur das Wort, das in diesen Indizes gefunden wurde, und nicht alle Vorkommen. Diese Lösung verwendet
StringBuilder
und produziert keine ZwischenprodukteString
wieString.replace()
.Eines ist zu beachten: Wenn die austauschbaren Wörter unterschiedliche Längen haben, kann sich nach dem ersten Ersetzen der zweite Index genau mit der Differenz der beiden Längen ändern (wenn das erste Wort vor dem zweiten vorkommt). Durch Ausrichten des zweiten Index wird sichergestellt, dass dies auch dann funktioniert, wenn Wörter mit unterschiedlichen Längen ausgetauscht werden.
Beliebige Anzahl von Vorkommen tauschen
Analog zum vorherigen Fall werden wir zuerst die Indizes (Vorkommen) der Wörter sammeln, aber in diesem Fall wird eine Liste von ganzen Zahlen für jedes Wort erstellt, nicht nur für eine
int
. Hierzu verwenden wir die folgende Dienstprogrammmethode:Und wenn wir dies verwenden, werden wir die Wörter durch die anderen ersetzen, indem wir den Index verringern (was möglicherweise einen Wechsel zwischen den beiden austauschbaren Wörtern erforderlich macht), damit wir nach einem Ersetzen nicht einmal die Indizes korrigieren müssen:
quelle
indexOf
übereinstimmende Teilzeichenfolge aufgrund der Besonderheiten der Unicode-Zeichenfolgenäquivalenz möglicherweise nicht die gleiche Länge wie die Suchzeichenfolge hat.String
ein Zeichenarray und kein Byte-Array ist. Alle Methoden vonString
undStringBuilder
arbeiten mit Zeichen, nicht mit Bytes, die "codierungsfrei" sind. SoindexOf
haben Streichhölzer genau das gleiche (Zeichen) Länge wie die Suchzeichenfolgen.ä
kann als einzelner Codepunkt oder alsa
gefolgt von einer Kombination codiert werden¨
. Es gibt auch einige Codepunkte, die ignoriert werden, z. B. (Nicht-) Joiner mit einer Breite von Null. Es spielt keine Rolle, ob die Zeichenfolge aus Bytes, Zeichen oder was auch immer besteht, sondern welche VergleichsregelnindexOf
verwendet werden. Es kann einfach Code-Einheit durch Code-Einheit-Vergleich ("Ordinal") verwenden oder Unicode-Äquivalenz implementieren. Ich weiß nicht, welchen Java gewählt hat."ab\u00ADc".IndexOf("bc")
kehrt1
in .net die zwei passende Zeichenkettebc
zu einer Drei - Zeichenfolge."ab\u00ADc".indexOf("bc")
zurückgegeben,-1
was bedeutet, dass"bc"
in nicht gefunden wurde"ab\u00ADc"
. Es steht also immer noch fest, dass in Java der obige Algorithmus funktioniert,indexOf()
Übereinstimmungen genau die gleiche (Zeichen-) Länge wieindexOf()
die Suchzeichenfolgen haben und nur dann Übereinstimmungen melden, wenn die Zeichenfolgen (Codepunkte) übereinstimmen.Es ist einfach, eine Methode zu schreiben, um dies zu tun
String.regionMatches
:Testen:
Ausgabe:
Es ist nicht sofort offensichtlich, aber eine Funktion wie diese kann immer noch von der Reihenfolge abhängen, in der die Ersetzungen angegeben sind. Erwägen:
Ausgabe:
Aber vertauschen Sie die Ersetzungen:
Ausgabe:
Hoppla! :) :)
Daher ist es manchmal nützlich, nach der längsten Übereinstimmung zu suchen (wie es beispielsweise die PHP-
strtr
Funktion tut). Diese Version der Methode wird das tun:Beachten Sie, dass bei den oben genannten Methoden zwischen Groß- und Kleinschreibung unterschieden wird. Wenn Sie eine Version benötigen, bei der die Groß- und Kleinschreibung nicht
String.regionMatches
berücksichtigt wird, können Sie die oben genannten Optionen leicht ändern, da sie einenignoreCase
Parameter annehmen können .quelle
Wenn Sie keine Abhängigkeiten möchten, können Sie einfach ein Array verwenden, das nur eine einmalige Änderung zulässt. Dies ist nicht die effizienteste Lösung, sollte aber funktionieren.
Dann würde es funktionieren.
quelle
Sie führen mehrere Such- / Ersetzungsvorgänge für die Eingabe aus. Dies führt zu unerwünschten Ergebnissen, wenn die Ersatzzeichenfolgen Suchzeichenfolgen enthalten. Betrachten Sie das Beispiel foo-> bar, bar-foo. Hier sind die Ergebnisse für jede Iteration:
Sie müssen den Austausch in einer Iteration durchführen, ohne zurück zu gehen. Eine Brute-Force-Lösung lautet wie folgt:
Eine Funktion wie
String.indexOfAny(String[]) -> int[]{index, whichString}
wäre nützlich. Hier ist ein Beispiel (nicht das effizienteste):Einige Tests:
Demo auf IDEONE
Demo auf IDEONE, alternativer Code
quelle
Sie können es jederzeit durch ein Wort ersetzen, von dem Sie sicher sind, dass es nirgendwo anders in der Zeichenfolge vorkommt, und dann das zweite Ersetzen später durchführen:
Beachten Sie, dass dies in diesem
"StringYouAreSureWillNeverOccur"
Fall nicht richtig funktioniert.quelle
Erwägen Sie die Verwendung von StringBuilder
Speichern Sie dann den Index, in dem jede Zeichenfolge beginnen soll. Wenn Sie an jeder Position ein Platzhalterzeichen verwenden, entfernen Sie es und fügen Sie die Benutzerzeichenfolge ein. Sie können dann die Endposition zuordnen, indem Sie die Zeichenfolgenlänge zur Startposition hinzufügen.
quelle
Was ich nur teilen kann, ist meine eigene Methode.
Sie können eine temporäre verwenden
String temp = "<?>";
oder verwendenString.Format();
Dies ist mein Beispielcode, der in der Konsolenanwendung über erstellt wurde c # - "Nur Idee, keine genaue Antwort" .
Oder Sie können auch die verwenden
String.Format();
Ausgabe:
time upon a Once, there was a bar and a foo.
quelle
temp
von"_"
in<?>
. Bei Bedarf kann er der Methode jedoch einen weiteren Parameter hinzufügen, der die Temperatur ändert. - "Es ist besser, es einfach zu halten, oder?"Hier ist meine Version, die wortbasiert ist:
quelle
Kleiner kniffliger Weg, aber Sie müssen noch einige Überprüfungen durchführen.
1.Konvertieren Sie eine Zeichenfolge in ein Zeichenarray
2.Schleifen Sie die Temperatur und ersetzen Sie sie
foo
durchbar
undbar
mit,foo
da keine Chance besteht, dass die Saite wieder ausgetauscht werden kann.quelle
Nun, die kürzere Antwort ist ...
quelle
Mit der hier gefundenen Antwort können Sie alle Vorkommen der Zeichenfolgen finden, durch die Sie ersetzen möchten.
So führen Sie beispielsweise den Code in der obigen SO-Antwort aus. Erstellen Sie zwei Indextabellen (sagen wir, Balken und Foo erscheinen nicht nur einmal in Ihrer Zeichenfolge), und Sie können mit diesen Tabellen arbeiten, um sie in Ihrer Zeichenfolge zu ersetzen.
Zum Ersetzen bestimmter Indexpositionen können Sie nun Folgendes verwenden:
Während
pos
ist der Index, wo Ihre Zeichenfolgen beginnen (aus den oben zitierten Indextabellen). Angenommen, Sie haben für jede Tabelle zwei Indextabellen erstellt. Nennen wir sieindexBar
undindexFoo
.Wenn Sie sie jetzt ersetzen, können Sie einfach zwei Schleifen ausführen, eine für jede Ersetzung, die Sie vornehmen möchten.
Ebenso eine andere Schleife für
indexFoo
.Dies ist möglicherweise nicht so effizient wie andere Antworten hier, aber es ist einfacher zu verstehen als Karten oder andere Dinge.
Dies würde Ihnen immer das gewünschte Ergebnis und für mehrere mögliche Vorkommen jeder Zeichenfolge liefern. Solange Sie den Index jedes Vorkommens speichern.
Auch diese Antwort erfordert weder eine Rekursion noch externe Abhängigkeiten. Was die Komplexität betrifft, ist es wahrscheinlich O (n im Quadrat), während n die Summe der Vorkommen beider Wörter ist.
quelle
Ich habe diesen Code entwickelt, um das Problem zu lösen:
In der Hauptanwendung
change(story,word2,word1).
quelle
quelle