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?


10
string.Join(",", yourCollection)? Edit: ditambahkan sebagai jawaban.
Vlad


@ 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.


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.


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)

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);

7

Saya lebih suka memanipulasi panjang stringbuilder:

data.Length = data.Length - 1;

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

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).


2

Ya, ubah menjadi string setelah loop selesai:

String str = data.ToString().TrimEnd(',');

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 )



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(',');

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.