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?
c#
stringbuilder
Wesley Skeen
źródło
źródło
string.Join(",", yourCollection)
? Edycja: dodana jako odpowiedź.Odpowiedzi:
Najprostszym i najbardziej wydajnym sposobem jest wykonanie tego polecenia:
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
StringBuilder
najlepiej również wykonaćLength
(ale w rzeczywistości użyjClear()
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…Length
cóż, 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.źródło
data.Length = 0;
: dokładnie toStringBuilder.Clear
robi, więc lepiej używać goStringBuilder.Clear
dla jasności intencji.Clear()
robi, ale zabawna rzecz. To jest pierwsza linia zClear()
metody. Ale czy wiesz, że interfejs faktycznie wyświetla plikreturn this;
. To właśnie mnie zabija. UstawiającLength = 0
zmiany jako odniesienie, które już masz, po co wracać?Append
wraca również.Po prostu użyj
string.Join(",", yourCollection)
W ten sposób nie potrzebujesz
StringBuilder
pę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.Join
przeciążenieIAsyncEnumerable<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 komentarzachSqlDataReader
), stosunkowo łatwo jest opakować dane wIAsyncEnumerable<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 nugetSystem.Interactive.Async
. Tutaj znajdziesz przykład do kompilacji.źródło
string.Join(",", yourCollection)
nadal ma,
na końcu. więc powyższy iestring.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)
źródło
string.Join(",", input)
Co powiesz na to..
string str = "The quick brown fox jumps over the lazy dog,"; StringBuilder sb = new StringBuilder(str); sb.Remove(str.Length - 1, 1);
źródło
Wolę manipulować długością stringbuildera:
data.Length = data.Length - 1;
źródło
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:
źródło
Należy użyć tej
string.Join
metody, aby zamienić kolekcję elementów w ciąg rozdzielany przecinkami. Zapewni to, że nie będzie żadnych przecinków na początku ani na końcu, a także zapewni wydajną konstrukcję ciągu (bez niepotrzebnych ciągów pośrednich).źródło
Tak, zamień go na ciąg po zakończeniu pętli:
String str = data.ToString().TrimEnd(',');
źródło
Masz dwie możliwości. Pierwsza to bardzo łatwa
Remove
metoda użycia , która jest dość skuteczna. Drugi sposób to użycieToString
z indeksem początkowym i końcowym ( dokumentacja MSDN )źródło
Podobne pytanie SO tutaj.
Podobało mi się używanie metody rozszerzenia StringBuilder.
RemoveLast Method
źródło
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(',');
źródło
data.ToString().TrimEnd(',');
jest nieefektywnyMam cię !!
Większość odpowiedzi w tym wątku nie zadziała, jeśli użyjesz
AppendLine
jak 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
Fiddle Me
CZEMU??? @ (& ** (& @ !!
Problem jest prosty, ale zajęło mi trochę czasu, zanim to rozgryzłem: ponieważ na końcu są jeszcze 2 niewidoczne znaki
CR
iLF
(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 -= 1
jeśli ostatnią wywołaną metodą byłaAppend
. Użyj,Length =- 3
jeśli używasz ostatniej metody, którą wywołałeśAppendLine
.źródło