Cara terbaik untuk menghapus karakter terakhir dari string yang dibangun dengan stringbuilder

92

Saya memiliki yang berikut ini

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

Masalahnya adalah saya menggunakannya dalam satu lingkaran dan akan ada koma uji coba. Apa cara terbaik untuk menghilangkan tanda koma?

Apakah saya harus mengubah data menjadi string substring itu?

Wesley Skeen
sumber
10
string.Join(",", yourCollection)? Edit: ditambahkan sebagai jawaban.
Vlad
1
apakah Anda mencoba stackoverflow.com/questions/5701163/… ?
andreister
@ Chris: dengan cara ini Anda tidak memerlukan StringBuilder sama sekali.
Vlad
mungkin Anda bisa menghindari menambahkan koma daripada menghapusnya setelahnya. Lihat: stackoverflow.com/questions/581448/… (Jawaban Jon Skeet)
Paolo Falabella
@Vlad Ya maaf, saya salah membaca itu; Saya pikir Anda menawarkannya sebagai saran untuk mengubah string yang dibangun terakhir, bukan sebagai pengganti loopnya sama sekali. (Saya pikir saya menghapus komentar saya tepat waktu, kira tidak!)
Chris Sinclair

Jawaban:

224

Cara termudah dan paling efisien adalah dengan melakukan perintah ini:

data.Length--;

dengan melakukan ini Anda memindahkan penunjuk (yaitu indeks terakhir) ke belakang satu karakter tetapi Anda tidak mengubah mutabilitas objek. Faktanya, membersihkan a StringBuilderpaling baik dilakukan dengan Lengthjuga (tetapi sebenarnya menggunakan Clear()metode untuk kejelasan sebagai gantinya karena seperti itulah implementasinya):

data.Length = 0;

sekali lagi, karena tidak mengubah tabel alokasi. Anggap saja seperti mengatakan, saya tidak ingin mengenali byte ini lagi. Sekarang, bahkan saat menelepon ToString(), ia tidak akan mengenali apa pun yang melewatinya Length, yah, ia tidak bisa. Ini adalah objek yang bisa berubah yang mengalokasikan lebih banyak ruang daripada yang Anda sediakan, itu dibuat dengan cara ini.

Mike Perrenoud
sumber
2
re data.Length = 0;: itulah yang StringBuilder.Cleardilakukannya, jadi lebih baik digunakan StringBuilder.Clearuntuk kejelasan niat.
Eren Ersönmez
@ ErenErsönmez, cukup adil teman, saya harus lebih jelas menyatakan itu yang Clear()dilakukan, tapi lucu. Itulah baris pertama dari Clear()metode. Tapi, tahukah Anda bahwa antarmuka sebenarnya kemudian mengeluarkan file return this;. Nah, itulah yang membunuh saya. Mengatur Length = 0perubahan referensi yang sudah Anda miliki, mengapa mengembalikan diri Anda sendiri?
Mike Perrenoud
12
Saya pikir itu untuk bisa digunakan dengan cara yang "lancar". Appendmengembalikan dirinya sendiri juga.
Eren Ersönmez
43

Gunakan saja

string.Join(",", yourCollection)

Dengan cara ini Anda tidak membutuhkan StringBuilderdan loop.




Penambahan panjang tentang kasus asinkron. Pada 2019, bukan hal yang jarang terjadi ketika data datang secara tidak sinkron.

Jika data Anda berada dalam koleksi asinkron, tidak ada string.Joinpengambilan berlebihan IAsyncEnumerable<T>. Tetapi mudah untuk membuatnya secara manual, meretas kode daristring.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();
        }
    }
}

Jika data datang secara asinkron tetapi antarmukanya IAsyncEnumerable<T>tidak didukung (seperti yang disebutkan di komentar SqlDataReader), relatif mudah untuk menggabungkan data menjadi 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]);
}

dan gunakan itu:

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

Untuk menggunakan Select, Anda harus menggunakan paket nuget System.Interactive.Async. Di sini Anda dapat menemukan contoh kompilasi.

Vlad
sumber
12
Jawaban terbaik bukanlah jawaban yang mengatasi masalah, tetapi jawaban yang mencegahnya.
LastTribunal
1
@ Seabizkit: Tentu saja! Seluruh pertanyaannya adalah tentang C #.
Vlad
1
@Vlad saya mengerti, hanya memeriksa dua kali karena saya melakukan tes sederhana seperti tes mentah dan mereka tidak menghasilkan yang sama. string.Join(",", yourCollection)masih memiliki ,akhir. jadi contoh di atas string.Join(",", yourCollection)tidak efisien dan tidak menghapusnya sendiri.
Seabizkit
2
@ Seabizkit: Sangat aneh! Bisakah Anda memposting contoh? Dalam kode saya, ini berfungsi dengan sempurna: ideone.com/aj8PWR
Vlad
2
Bertahun-tahun menggunakan loop untuk membangun pembuat string dan kemudian menghapus tanda koma dan saya bisa saja menggunakan ini. Terima kasih atas tipnya!
Caverman
11

Gunakan yang berikut ini setelah pengulangan.

.TrimEnd(',')

atau cukup ubah menjadi

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)
Sam Leach
sumber
4
Dia menggunakan StringBuilder bukan string. Selain itu, ini cukup tidak efektif: konversi pertama ke string kemudian pemangkasan.
Piotr Stapp
ataustring.Join(",", input)
Tvde1
11

Bagaimana dengan ini..

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

Saya lebih suka memanipulasi panjang stringbuilder:

data.Length = data.Length - 1;
bastos.sergio
sumber
4
Mengapa tidak sederhana data.Length--atau --data.Length?
Pergi Coding
Saya biasanya menggunakan data.Length - tetapi dalam satu kasus saya harus mundur 2 karakter karena nilai kosong setelah karakter yang ingin saya hapus. Trim tidak bekerja dalam kasus itu, jadi data.Length = data.Length - 2; bekerja.
Caverman
Trim mengembalikan instance baru dari string, itu tidak mengubah konten objek stringbuilder
bastos.sergio
@GoneCoding Visual Basic .NET tidak mendukung --atau ++ Anda dapat menggunakannya data.Length -= 1, atau jawaban ini juga akan berhasil.
Jason S
3

Saya sarankan, Anda mengubah algoritma loop Anda:

  • Tambahkan koma bukan SETELAH item, tetapi SEBELUM
  • Gunakan variabel boolean, yang dimulai dengan false, jangan tekan koma pertama
  • Setel variabel boolean ini ke true setelah mengujinya
Eugen Rieck
sumber
2
Ini mungkin yang paling tidak efisien dari semua saran (dan membutuhkan lebih banyak kode).
Pergi Coding
1
Lihat jawaban @Vlad
Noctis
3

Anda harus menggunakan string.Joinmetode ini untuk mengubah kumpulan item menjadi string yang dipisahkan koma. Ini akan memastikan bahwa tidak ada koma di depan atau di belakang, serta memastikan string dibangun secara efisien (tanpa string perantara yang tidak perlu).

Pelayanan
sumber
2

Ya, ubah menjadi string setelah loop selesai:

String str = data.ToString().TrimEnd(',');
DonBoitnott
sumber
3
ini cukup tidak efektif: konversi pertama ke string kemudian pemangkasan.
Piotr Stapp
2
@ Garath Jika yang Anda maksud "tidak efisien", saya tidak akan setuju. Tapi itu akan efektif.
DonBoitnott
2

Anda punya dua pilihan. Yang pertama adalah Removemetode penggunaan yang sangat mudah , cukup efektif. Cara kedua adalah menggunakan ToStringindeks awal dan indeks akhir ( dokumentasi MSDN )

Piotr Stapp
sumber
1

Cara paling sederhana adalah dengan menggunakan metode Join ():

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

Jika Anda benar-benar membutuhkan StringBuilder, potong koma akhir setelah pengulangan:

data.ToString().TrimEnd(',');
studert
sumber
4
data.ToString().TrimEnd(',');tidak efisien
bastos.sergio
1
Selain itu, Anda mungkin tidak ingin mengonversi objek StringBuilder menjadi String karena mungkin memiliki beberapa baris yang diakhiri dengan ","
Fandango68
0

Kena kau!!

Sebagian besar jawaban di utas ini tidak akan berfungsi jika Anda menggunakan AppendLineseperti di bawah ini:

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

MENGAPA??? @ (& ** (& @ !!

Masalahnya sederhana tapi butuh beberapa saat untuk mengetahuinya: Karena ada 2 karakter yang tidak terlihat di akhir CRdan LF(Carriage Return dan Line Feed). Oleh karena itu, Anda perlu menghilangkan 3 karakter terakhir:

var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length -= 3; // This will work
Console.WriteLine(builder.ToString());

Kesimpulannya

Gunakan Length--atau Length -= 1jika metode terakhir yang Anda panggil adalah Append. Gunakan Length =- 3jika Anda metode terakhir yang Anda panggil AppendLine.

CodingYoshi
sumber