Ersetzen Sie mehrere Zeichenfolgenelemente in C #

86

Gibt es einen besseren Weg, dies zu tun ...

MyString.Trim().Replace("&", "and").Replace(",", "").Replace("  ", " ")
         .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower();

Ich habe die String-Klasse erweitert, um sie auf einen Job zu beschränken, aber gibt es einen schnelleren Weg?

public static class StringExtension
{
    public static string clean(this string s)
    {
        return s.Replace("&", "and").Replace(",", "").Replace("  ", " ")
                .Replace(" ", "-").Replace("'", "").Replace(".", "")
                .Replace("eacute;", "é").ToLower();
    }
}

Nur zum Spaß (und um die Argumente in den Kommentaren zu stoppen) habe ich die verschiedenen Beispiele unten auf den Punkt gebracht.

https://gist.github.com/ChrisMcKee/5937656

Die Regex-Option punktet furchtbar. Die Wörterbuchoption wird am schnellsten angezeigt. Die langatmige Version des Stringbuilder-Ersatzes ist etwas schneller als die kurze Hand.

Chris McKee
quelle
1
Basierend auf dem, was Sie in Ihren Benchmarks haben, sieht es so aus, als würde die Wörterbuchversion nicht alle Ersetzungen durchführen, von denen ich vermute, dass sie schneller sind als die StringBuilder-Lösungen.
Kröte
1
@toad Hi von 2009; Ich habe unten im April einen Kommentar zu diesem grellen Fehler hinzugefügt. Das Wesentliche wird aktualisiert, obwohl ich D übersprungen habe. Die Wörterbuchversion ist immer noch schneller.
Chris McKee
Mögliches Duplikat von Alternative zu String. Mehrmals ersetzen?
Tot Zam
1
@TotZam überprüfe mindestens die Daten, bevor du Dinge kennzeichnest; Dies ist aus dem Jahr 2009, das ist aus dem Jahr 2012
Chris McKee
Da sich viele Antworten hier auf die Leistung beziehen, sollte meines Erachtens darauf hingewiesen werden, dass die Antwort von Andrej Adamanko bei vielen Ersetzungen wahrscheinlich die schnellste ist. sicherlich schneller als die Verkettung .Replace (), insbesondere bei einer großen Eingabezeichenfolge, wie in seiner Antwort angegeben.
Person27

Antworten:

123

Schneller - nein. Effektiver - ja, wenn Sie die StringBuilderKlasse verwenden. Bei Ihrer Implementierung generiert jede Operation eine Kopie einer Zeichenfolge, die unter Umständen die Leistung beeinträchtigen kann. Zeichenfolgen sind unveränderliche Objekte, sodass jede Operation nur eine geänderte Kopie zurückgibt.

Wenn Sie erwarten, dass diese Methode für mehrere Stringsvon erheblicher Länge aktiv aufgerufen wird , ist es möglicherweise besser, ihre Implementierung auf die StringBuilderKlasse zu "migrieren" . Damit wird jede Änderung direkt an dieser Instanz durchgeführt, sodass Sie unnötige Kopiervorgänge vermeiden.

public static class StringExtention
{
    public static string clean(this string s)
    {
        StringBuilder sb = new StringBuilder (s);

        sb.Replace("&", "and");
        sb.Replace(",", "");
        sb.Replace("  ", " ");
        sb.Replace(" ", "-");
        sb.Replace("'", "");
        sb.Replace(".", "");
        sb.Replace("eacute;", "é");

        return sb.ToString().ToLower();
    }
}
BC2
quelle
2
Aus Gründen der Klarheit ist die Wörterbuchantwort die schnellste stackoverflow.com/a/1321366/52912
Chris McKee
3
In Ihrem Benchmark auf gist.github.com/ChrisMcKee/5937656 ist der Wörterbuchtest nicht vollständig: Es werden nicht alle Ersetzungen durchgeführt und "" ersetzt "", nicht "". Nicht alle Ersetzungen könnten der Grund sein, warum es im Benchmark am schnellsten ist. Der Regex-Austausch ist ebenfalls nicht vollständig. Am wichtigsten ist jedoch, dass Ihre Zeichenfolge TestData sehr kurz ist. Wie die akzeptierten Antwortzustände muss die Zeichenfolge eine signifikante Länge haben, damit der StringBuilder von Vorteil ist. Könnten Sie bitte den Benchmark mit Strings von 10 KB, 100 KB und 1 MB wiederholen?
Leif
Es ist ein guter Punkt; So wie es aussieht, wurde es für die URL-Bereinigung verwendet, daher wären Tests bei 100 KB - 1 MB unrealistisch gewesen. Ich werde den Benchmark aktualisieren, damit er das Ganze verwendet, das war ein Fehler.
Chris McKee
Um eine optimale Leistung zu erzielen, durchlaufen Sie die Zeichen und ersetzen Sie sie selbst. Dies kann jedoch mühsam sein, wenn Sie mehr als einzelne Zeichenfolgen haben (wenn Sie feststellen, dass Sie mehrere Zeichen gleichzeitig vergleichen müssen, müssen Sie beim Ersetzen mehr Speicher zuweisen und den Rest der Zeichenfolge verschieben).
Chayim Friedman
13

das wird effizienter:

public static class StringExtension
{
    public static string clean(this string s)
    {
        return new StringBuilder(s)
              .Replace("&", "and")
              .Replace(",", "")
              .Replace("  ", " ")
              .Replace(" ", "-")
              .Replace("'", "")
              .Replace(".", "")
              .Replace("eacute;", "é")
              .ToString()
              .ToLower();
    }
}
TheVillageIdiot
quelle
Wirklich schwer zu lesen. Ich bin sicher, Sie wissen, was es tut, aber ein Junior-Entwickler wird sich am Kopf kratzen, was tatsächlich vor sich geht. Ich stimme zu - ich suche auch immer nach der kurzen Hand, um etwas zu schreiben -, aber es war nur zu meiner eigenen Zufriedenheit. Andere Leute flippten auf dem Haufen Chaos aus.
Piotr Kula
3
Das ist eigentlich langsamer. BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms Variiert bei Wiederholungen, aber die Antwort gewinnt gist.github.com/anonymous/5937596
Chris McKee
11

Vielleicht etwas lesbarer?

    public static class StringExtension {

        private static Dictionary<string, string> _replacements = new Dictionary<string, string>();

        static StringExtension() {
            _replacements["&"] = "and";
            _replacements[","] = "";
            _replacements["  "] = " ";
            // etc...
        }

        public static string clean(this string s) {
            foreach (string to_replace in _replacements.Keys) {
                s = s.Replace(to_replace, _replacements[to_replace]);
            }
            return s;
        }
    }

Fügen Sie auch den Vorschlag von New In Town zu StringBuilder hinzu ...

Paolo Tedesco
quelle
5
Es wäre so besser lesbar:private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves hält SE für böse
2
oder natürlich ... privates statisches schreibgeschütztes Wörterbuch <Zeichenfolge, Zeichenfolge> Replacements = neues Wörterbuch <Zeichenfolge, Zeichenfolge> () {{"&", "und"}, {",", ""}, {"", " " } /* etc */ }; öffentliche statische Zeichenfolge Clean (diese Zeichenfolge s) {return Replacements.Keys.Aggregate (s, (current, toReplace) => current.Replace (toReplace, Replacements [toReplace])); }
Chris McKee
2
-1: Die Verwendung eines Wörterbuchs macht hier keinen Sinn. Verwenden Sie einfach eine List<Tuple<string,string>>. Dies ändert auch die Reihenfolge der Ersetzungen und ist nicht so schnell wie z s.Replace("a").Replace("b").Replace("c"). Benutze das nicht!
Thomas
11

Wenn Sie einfach nach einer hübschen Lösung suchen und nicht ein paar Nanosekunden sparen müssen, wie wäre es dann mit etwas LINQ-Zucker?

var input = "test1test2test3";
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } };

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value));
TimS
quelle
Ähnlich wie in Beispiel C im Kern (wenn Sie darüber schauen, ist die hässlichere linq-Aussage im Kommentar)
Chris McKee
1
Interessant, dass Sie eine funktionale Aussage als "hässlicher" als eine prozedurale definieren.
TimS
nicht darüber streiten; seine bloße Präferenz. Wie Sie sagen, ist linq einfach syntaktischer Zucker; und wie gesagt ich hatte schon das Äquivalent über den Code gesetzt :)
Chris McKee
6

Es gibt eine Sache, die in den vorgeschlagenen Lösungen optimiert werden kann. Wenn Sie viele Aufrufe an haben, Replace()führt der Code mehrere Durchgänge über dieselbe Zeichenfolge aus. Bei sehr langen Zeichenfolgen können die Lösungen aufgrund fehlender CPU-Cache-Kapazität langsam sein. Möglicherweise sollte man in Betracht ziehen , mehrere Zeichenfolgen in einem einzigen Durchgang zu ersetzen .

Andrej Adamenko
quelle
1
Viele Antworten scheinen besorgt über die Leistung zu sein. In diesem Fall ist dies die beste. Und es ist einfach, weil es sich nur um eine dokumentierte Überladung von String.Replace handelt, bei der Sie einen erwarteten Wert basierend auf der Übereinstimmung zurückgeben. In diesem Beispiel verwenden Sie ein Wörterbuch, um sie abzugleichen. Sollte einfach zu verstehen sein.
Person27
4

Eine andere Option mit linq ist

[TestMethod]
public void Test()
{
  var input = "it's worth a lot of money, if you can find a buyer.";
  var expected = "its worth a lot of money if you can find a buyer";
  var removeList = new string[] { ".", ",", "'" };
  var result = input;

  removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty));

  Assert.AreEqual(expected, result);
}
Luiz Felipe
quelle
Sie können var removeList = new List<string> { /*...*/ };dann einfach removeList.ForEach( /*...*/ );Ihren Code aufrufen und vereinfachen. Beachten Sie auch, dass die Frage nicht vollständig beantwortet wird, da alle gefundenen Zeichenfolgen durch ersetzt werden String.Empty.
Tok
2

Ich mache etwas Ähnliches, aber in meinem Fall mache ich Serialisierung / De-Serialisierung, also muss ich in beide Richtungen gehen können. Ich finde, dass die Verwendung eines Strings [] [] fast identisch mit dem Wörterbuch funktioniert, einschließlich der Initialisierung, aber Sie können auch in die andere Richtung gehen und die Substitute auf ihre ursprünglichen Werte zurücksetzen, etwas, für das das Wörterbuch wirklich nicht eingerichtet ist.

Bearbeiten: Sie können verwenden Dictionary<Key,List<Values>>, um das gleiche Ergebnis wie Zeichenfolge [] [] zu erhalten.

sidDemure
quelle
-1
string input = "it's worth a lot of money, if you can find a buyer.";
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length / 2; i++) {
    input = input.Replace(repl[i, 0], repl[i, 1]);
}
user7718176
quelle
2
Sie sollten erwägen, Ihren Antworten Kontext hinzuzufügen. Wie eine kurze Erklärung, was es tut und, falls relevant, warum Sie es so geschrieben haben, wie Sie es getan haben.
Neil