Mam następujące
data.AppendFormat("{0},",dataToAppend);
Problem polega na tym, że używam go w pętli i pojawi się przecinek testowy. Jaki jest najlepszy sposób na usunięcie końcowego przecinka?
Czy muszę zmienić dane na ciąg będący podłańcuchem?
Mam następujące
data.AppendFormat("{0},",dataToAppend);
Problem polega na tym, że używam go w pętli i pojawi się przecinek testowy. Jaki jest najlepszy sposób na usunięcie końcowego przecinka?
Czy muszę zmienić dane na ciąg będący podłańcuchem?
Odpowiedzi:
Najprostszym i najbardziej wydajnym sposobem jest wykonanie tego polecenia:
data.Length--;
w ten sposób przesuwasz wskaźnik (tj. ostatni indeks) wstecz o jeden znak, ale nie zmieniasz zmienności obiektu. W rzeczywistości, wyczyszczenie a StringBuildernajlepiej również wykonać Length(ale w rzeczywistości użyj Clear()metody dla przejrzystości, ponieważ tak wygląda jej implementacja):
data.Length = 0;
ponownie, ponieważ nie zmienia to tabeli alokacji. Pomyśl o tym jak o stwierdzeniu, że nie chcę już rozpoznawać tych bajtów. Teraz, nawet dzwoniąc ToString(), nie rozpozna niczego poza swoim… Lengthcóż, nie może. Jest to zmienny obiekt, który przydziela więcej miejsca niż to, co mu zapewniasz, jest po prostu zbudowany w ten sposób.
data.Length = 0;: dokładnie to StringBuilder.Clearrobi, więc lepiej używać go StringBuilder.Cleardla jasności intencji.
Clear()robi, ale zabawna rzecz. To jest pierwsza linia z Clear()metody. Ale czy wiesz, że interfejs faktycznie wyświetla plik return this;. To właśnie mnie zabija. Ustawiając Length = 0zmiany jako odniesienie, które już masz, po co wracać?
Appendwraca również.
Po prostu użyj
string.Join(",", yourCollection)
W ten sposób nie potrzebujesz StringBuilderpętli i.
Długi dodatek o przypadku asynchronicznym. Począwszy od 2019 roku, nie jest to rzadka konfiguracja, gdy dane przychodzą asynchronicznie.
W przypadku, gdy Twoje dane znajdują się w kolekcji asynchronicznej, nie występuje string.Joinprzeciążenie IAsyncEnumerable<T>. Ale łatwo jest utworzyć go ręcznie, hakując kod zstring.Join :
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();
}
}
}
Jeśli dane przychodzą asynchronicznie, ale interfejs IAsyncEnumerable<T>nie jest obsługiwany (jak wspomniano w komentarzach SqlDataReader), stosunkowo łatwo jest opakować dane w 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]);
}
i użyj go:
Task<string> Stringify(SqlDataReader reader) =>
StringEx.JoinAsync(
", ",
ExtractData(reader).Select(x => $"{x.first} * {x.second} = {x.product}"));
Aby użyć Select, musisz użyć pakietu nuget System.Interactive.Async. Tutaj znajdziesz przykład do kompilacji.
string.Join(",", yourCollection)nadal ma ,na końcu. więc powyższy ie string.Join(",", yourCollection)jest nieefektywny i sam go nie usuwa.
Użyj następującego po pętli.
.TrimEnd(',')
lub po prostu zmień na
string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)
string.Join(",", input)
Wolę manipulować długością stringbuildera:
data.Length = data.Length - 1;
data.Length--lub --data.Length?
--lub ++ możesz użyć data.Length -= 1, albo ta odpowiedź też będzie działać.
Polecam zmienić algorytm pętli:
Tak, zamień go na ciąg po zakończeniu pętli:
String str = data.ToString().TrimEnd(',');
Masz dwie możliwości. Pierwsza to bardzo łatwa Removemetoda użycia , która jest dość skuteczna. Drugi sposób to użycie ToStringz indeksem początkowym i końcowym ( dokumentacja MSDN )
Podobało mi się używanie metody rozszerzenia StringBuilder.
Najprostszym sposobem byłoby użycie metody Join ():
public static void Trail()
{
var list = new List<string> { "lala", "lulu", "lele" };
var data = string.Join(",", list);
}
Jeśli naprawdę potrzebujesz StringBuilder, przytnij końcowy przecinek po pętli:
data.ToString().TrimEnd(',');
data.ToString().TrimEnd(',');jest nieefektywny
Większość odpowiedzi w tym wątku nie zadziała, jeśli użyjesz AppendLinejak poniżej:
var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length--; // Won't work
Console.Write(builder.ToString());
builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length += -1; // Won't work
Console.Write(builder.ToString());
builder = new StringBuilder();
builder.AppendLine("One,");
Console.Write(builder.TrimEnd(',')); // Won't work
CZEMU??? @ (& ** (& @ !!
Problem jest prosty, ale zajęło mi trochę czasu, zanim to rozgryzłem: ponieważ na końcu są jeszcze 2 niewidoczne znaki CRi LF(powrót karetki i przesunięcie wiersza). Dlatego musisz usunąć 3 ostatnie znaki:
var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length -= 3; // This will work
Console.WriteLine(builder.ToString());
Na zakończenie
Użyj Length--lub, Length -= 1jeśli ostatnią wywołaną metodą była Append. Użyj, Length =- 3jeśli używasz ostatniej metody, którą wywołałeś AppendLine.
string.Join(",", yourCollection)? Edycja: dodana jako odpowiedź.