Der beste Weg, um das letzte Zeichen aus einer mit Stringbuilder erstellten Zeichenfolge zu entfernen

89

Ich habe folgendes

data.AppendFormat("{0},",dataToAppend);

Das Problem dabei ist, dass ich es in einer Schleife verwende und es ein Komma gibt. Was ist der beste Weg, um das nachfolgende Komma zu entfernen?

Muss ich Daten in eine Zeichenfolge ändern, die Teilzeichenfolge enthält?

Wesley Skeen
quelle
10
string.Join(",", yourCollection)? Bearbeiten: als Antwort hinzugefügt.
Vlad
1
Haben Sie stackoverflow.com/questions/5701163/… ausprobiert ?
andreister
@Chris: Auf diese Weise benötigen Sie überhaupt keinen StringBuilder.
Vlad
Vielleicht können Sie das Hinzufügen des Kommas vermeiden, anstatt es anschließend zu entfernen. Siehe: stackoverflow.com/questions/581448/… (Jon Skeets Antwort)
Paolo Falabella
@Vlad Ja sorry, ich habe das falsch verstanden; Ich dachte, Sie bieten es als Vorschlag an, die endgültig gebaute Saite zu ändern, und nicht als Ersatz für seine Schleife insgesamt. (Ich dachte, ich hätte meinen Kommentar rechtzeitig gelöscht, rate nicht!)
Chris Sinclair

Antworten:

219

Der einfachste und effizienteste Weg besteht darin, diesen Befehl auszuführen:

data.Length--;

Auf diese Weise verschieben Sie den Zeiger (dh den letzten Index) um ein Zeichen zurück, ändern jedoch nicht die Veränderlichkeit des Objekts. Tatsächlich ist das Löschen von a StringBuilderam besten auch mit Length(verwenden Sie die Clear()Methode jedoch aus Gründen der Klarheit, da die Implementierung so aussieht):

data.Length = 0;

wieder, weil es die Zuordnungstabelle nicht ändert. Stellen Sie sich vor, Sie möchten diese Bytes nicht mehr erkennen. Selbst wenn er anruft ToString(), erkennt er nichts mehr als das , was Lengther kann. Es ist ein veränderliches Objekt, das mehr Speicherplatz zuweist als Sie bereitstellen. Es ist einfach so aufgebaut.

Mike Perrenoud
quelle
2
re data.Length = 0;: Das ist genau das StringBuilder.Clear, was es tut, daher ist es StringBuilder.Clearaus Gründen der Klarheit der Absicht vorzuziehen, es zu verwenden .
Eren Ersönmez
@ ErenErsönmez, fair genug Freund, ich hätte klarer sagen sollen, was das Clear()macht, aber lustige Sache. Das ist die erste Zeile der Clear()Methode. Aber wussten Sie, dass die Schnittstelle tatsächlich dann a ausgibt return this;. Das bringt mich um. Wenn Sie die Length = 0Änderungen auf die Referenz einstellen, die Sie bereits haben, warum sollten Sie sich selbst zurückgeben?
Mike Perrenoud
12
Ich denke, es ist für die "fließende" Verwendung. Appendkehrt auch zurück.
Eren Ersönmez
42

Benutz einfach

string.Join(",", yourCollection)

Auf diese Weise benötigen Sie die StringBuilderund die Schleife nicht.




Langer Zusatz zum asynchronen Fall. Ab 2019 ist es keine seltene Einrichtung, wenn die Daten asynchron kommen.

Wenn sich Ihre Daten in einer asynchronen Sammlung befinden, erfolgt keine string.JoinÜberlastung IAsyncEnumerable<T>. Es ist jedoch einfach, einen manuell zu erstellen und den Code zustring.Join hacken von :

public static class StringEx
{
    public static async Task<string> JoinAsync<T>(string separator, IAsyncEnumerable<T> seq)
    {
        if (seq == null)
            throw new ArgumentNullException(nameof(seq));

        await using (var en = seq.GetAsyncEnumerator())
        {
            if (!await en.MoveNextAsync())
                return string.Empty;

            string firstString = en.Current?.ToString();

            if (!await en.MoveNextAsync())
                return firstString ?? string.Empty;

            // Null separator and values are handled by the StringBuilder
            var sb = new StringBuilder(256);
            sb.Append(firstString);

            do
            {
                var currentValue = en.Current;
                sb.Append(separator);
                if (currentValue != null)
                    sb.Append(currentValue);
            }
            while (await en.MoveNextAsync());
            return sb.ToString();
        }
    }
}

Wenn die Daten asynchron kommen, die Schnittstelle IAsyncEnumerable<T>jedoch nicht unterstützt wird (wie in den Kommentaren erwähnt SqlDataReader), ist es relativ einfach, die Daten in Folgendes zu verpacken IAsyncEnumerable<T>:

async IAsyncEnumerable<(object first, object second, object product)> ExtractData(
        SqlDataReader reader)
{
    while (await reader.ReadAsync())
        yield return (reader[0], reader[1], reader[2]);
}

und benutze es:

Task<string> Stringify(SqlDataReader reader) =>
    StringEx.JoinAsync(
        ", ",
        ExtractData(reader).Select(x => $"{x.first} * {x.second} = {x.product}"));

Um verwenden zu können Select, müssen Sie das Nuget-Paket verwenden System.Interactive.Async. Hier finden Sie ein kompilierbares Beispiel.

Vlad
quelle
12
Die beste Antwort ist nicht die, die das Problem angeht, sondern die, die es verhindert.
LastTribunal
1
@ Seizizkit: Natürlich! Die ganze Frage bezieht sich übrigens auf C #.
Vlad
1
@Vlad Ich verstehe das, nur doppelte Überprüfung, da ich einen einfachen Test wie einen Rohtest mache und sie nicht das gleiche produzieren. string.Join(",", yourCollection)hat noch ein ,am ende. Das oben Genannte string.Join(",", yourCollection)ist also ineffizient und entfernt es nicht von alleine.
Seabizkit
2
@ Seizizkit: Sehr seltsam! Könnten Sie ein Beispiel posten? In meinem Code funktioniert es perfekt: ideone.com/aj8PWR
Vlad
2
Jahre der Verwendung einer Schleife, um einen String-Builder zu erstellen und dann das nachfolgende Komma zu entfernen, und ich hätte dies einfach verwenden können. Danke für den Tipp!
Caverman
11

Verwenden Sie nach der Schleife Folgendes.

.TrimEnd(',')

oder einfach zu wechseln

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)
Sam Leach
quelle
4
Er benutzt StringBuilder, keinen String. Außerdem ist es ziemlich ineffektiv: zuerst in String konvertieren, dann trimmen.
Piotr Stapp
oderstring.Join(",", input)
Tvde1
10

Wie wäre es damit..

string str = "The quick brown fox jumps over the lazy dog,";
StringBuilder sb = new StringBuilder(str);
sb.Remove(str.Length - 1, 1);
Pankaj
quelle
7

Ich bevorzuge es, die Länge des Stringbuilders zu manipulieren:

data.Length = data.Length - 1;
bastos.sergio
quelle
4
Warum nicht einfach data.Length--oder --data.Length?
Gone Coding
Normalerweise verwende ich data.Length--, aber in einem Fall musste ich 2 Zeichen wegen eines leeren Werts nach dem Zeichen, das ich entfernen wollte, zurück verschieben. Das Trimmen hat auch in diesem Fall nicht funktioniert, also data.Length = data.Length - 2; hat funktioniert.
Caverman
Trim gibt eine neue Instanz eines Strings zurück und ändert den Inhalt des Stringbuilder-Objekts nicht
bastos.sergio
@GoneCoding Visual Basic .NET wird nicht unterstützt --oder ++ Sie können es jedoch verwenden data.Length -= 1, oder diese Antwort funktioniert auch.
Jason S
3

Ich empfehle Ihnen, Ihren Schleifenalgorithmus zu ändern:

  • Fügen Sie das Komma nicht NACH dem Element hinzu, sondern VOR
  • Verwenden Sie eine boolesche Variable, die mit false beginnt. Unterdrücken Sie das erste Komma
  • Setzen Sie diese boolesche Variable nach dem Testen auf true
Eugen Rieck
quelle
2
Dies ist wahrscheinlich der am wenigsten effiziente aller Vorschläge (und erfordert mehr Code).
Gone Coding
1
Schauen Sie sich @Vlad Antwort
Noctis
3

Sie sollten die string.JoinMethode verwenden, um eine Sammlung von Elementen in eine durch Kommas getrennte Zeichenfolge umzuwandeln. Dadurch wird sichergestellt, dass kein führendes oder nachfolgendes Komma vorhanden ist, und es wird sichergestellt, dass die Zeichenfolge effizient aufgebaut ist (ohne unnötige Zwischenzeichenfolgen).

Servieren
quelle
2

Ja, konvertieren Sie es nach Abschluss der Schleife in einen String:

String str = data.ToString().TrimEnd(',');
DonBoitnott
quelle
3
es ist ziemlich ineffektiv: zuerst in einen String konvertieren, dann zuschneiden.
Piotr Stapp
2
@Garath Wenn du "ineffizient" meinst, würde ich nicht widersprechen. Aber es wäre effektiv.
DonBoitnott
2

Sie haben zwei Möglichkeiten. Erstens ist eine sehr einfach zu bedienenRemove Methode, die sehr effektiv ist. Die zweite Möglichkeit ist die Verwendung ToStringmit Startindex und Endindex ( MSDN-Dokumentation ).

Piotr Stapp
quelle
1

Am einfachsten wäre es, die Join () -Methode zu verwenden:

public static void Trail()
{
    var list = new List<string> { "lala", "lulu", "lele" };
    var data = string.Join(",", list);
}

Wenn Sie den StringBuilder wirklich benötigen, schneiden Sie das Endkomma nach der Schleife ab:

data.ToString().TrimEnd(',');
studert
quelle
4
data.ToString().TrimEnd(',');ist ineffizient
bastos.sergio
1
Möglicherweise möchten Sie das StringBuilder-Objekt auch nicht in String konvertieren, da es möglicherweise mehrere Zeilen enthält, die mit "," enden
Fandango68