Cách tốt nhất để xóa ký tự cuối cùng khỏi chuỗi được xây dựng bằng trình tạo chuỗi


90

Tôi có cái sau

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

Vấn đề với điều này là tôi đang sử dụng nó trong một vòng lặp và sẽ có một dấu phẩy nhỏ. Cách tốt nhất để xóa dấu phẩy ở cuối là gì?

Tôi có phải thay đổi dữ liệu thành một chuỗi thành chuỗi con của nó không?


10
string.Join(",", yourCollection)? Chỉnh sửa: được thêm vào dưới dạng câu trả lời.
Vlad


@Chris: theo cách này bạn không cần StringBuilder gì cả.
Vlad

có thể bạn có thể tránh thêm dấu phẩy thay vì xóa nó sau đó. Xem: stackoverflow.com/questions/581448/... (Jon Skeet câu trả lời)
Paolo Falabella

@Vlad Vâng xin lỗi, tôi đã đọc nhầm điều đó; Tôi nghĩ rằng bạn đang cung cấp nó như một gợi ý để thay đổi chuỗi được xây dựng cuối cùng, chứ không phải để thay thế hoàn toàn cho vòng lặp của anh ấy. (Tôi nghĩ tôi đã xóa nhận xét của tôi trong thời gian, đoán không!)
Chris Sinclair

Câu trả lời:


222

Cách đơn giản và hiệu quả nhất là thực hiện lệnh này:

data.Length--;

bằng cách này, bạn di chuyển con trỏ (tức là chỉ mục cuối cùng) trở lại một ký tự nhưng bạn không thay đổi khả năng thay đổi của đối tượng. Trên thực tế, việc xóa a StringBuildercũng được thực hiện tốt nhất Length(nhưng thực sự nên sử dụng Clear()phương pháp này để rõ ràng thay vì đó là cách triển khai của nó):

data.Length = 0;

một lần nữa, vì nó không thay đổi bảng phân bổ. Hãy nghĩ về nó như nói rằng, tôi không muốn nhận ra những byte này nữa. Bây giờ, ngay cả khi gọi ToString(), nó sẽ không nhận ra bất cứ điều gì đã qua của nó Length, tốt, nó không thể. Đó là một đối tượng có thể thay đổi phân bổ nhiều không gian hơn những gì bạn cung cấp cho nó, nó được xây dựng đơn giản theo cách này.


2
re data.Length = 0;: đó chính xác là những gì StringBuilder.Clearhiện, vì vậy nó được ưu tiên sử dụng StringBuilder.Clearđể làm rõ ý định.
Eren Ersönmez

@ ErenErsönmez, công bằng mà nói, tôi nên nói rõ hơn đó là những gì Clear()có, nhưng điều buồn cười. Đó là dòng đầu tiên của Clear()phương pháp. Nhưng, bạn có biết giao diện thực sự sau đó các vấn đề a return this;. Bây giờ đó là thứ giết chết tôi. Đặt các Length = 0thay đổi tham chiếu bạn đã có, tại sao lại tự quay lại?
Mike Perrenoud

12
Tôi nghĩ rằng nó có thể sử dụng một cách "trôi chảy". Appendcũng trả về chính nó.
Eren Ersönmez

43

Chỉ dùng

string.Join(",", yourCollection)

Bằng cách này, bạn không cần StringBuildervà vòng lặp.




Bổ sung dài về trường hợp không đồng bộ. Kể từ năm 2019, đây không phải là một thiết lập hiếm khi dữ liệu đến không đồng bộ.

Trong trường hợp dữ liệu của bạn đang được thu thập không đồng bộ, không có string.Joinquá tải IAsyncEnumerable<T>. Nhưng thật dễ dàng để tạo một cái theo cách thủ công, lấy mã từstring.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();
        }
    }
}

Nếu dữ liệu đến không đồng bộ nhưng giao diện IAsyncEnumerable<T>không được hỗ trợ (như được đề cập trong nhận xét SqlDataReader), thì việc bọc dữ liệu thành mộtIAsyncEnumerable<T> :

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

và sử dụng nó:

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

Để sử dụng Select, bạn sẽ cần sử dụng gói nuget System.Interactive.Async. Ở đây bạn có thể tìm thấy một ví dụ có thể tổng hợp được.


12
Câu trả lời tốt nhất không phải là giải quyết vấn đề, mà là ngăn chặn nó.
LastTribunal

1
@Seabizkit: Tất nhiên! Nhân tiện, toàn bộ câu hỏi là về C #.
Vlad

1
@Vlad tôi hiểu rồi, chỉ kiểm tra hai lần vì tôi đang thực hiện kiểm tra đơn giản như kiểm tra thô và chúng không tạo ra giống nhau. string.Join(",", yourCollection)vẫn có một ,ở cuối. do đó, nghĩa là ở trên string.Join(",", yourCollection)không hiệu quả và không tự loại bỏ nó.
Seabizkit

2
@Seabizkit: Rất lạ! Bạn có thể đăng một ví dụ? Trong mã của tôi, nó hoạt động hoàn hảo: Ideone.com/aj8PWR
Vlad

2
Nhiều năm sử dụng vòng lặp để xây dựng trình tạo chuỗi và sau đó xóa dấu phẩy ở cuối và tôi có thể đã sử dụng điều này. Cảm ơn vì tiền hỗ trợ!
Caverman 21/09/17

11

Sử dụng phần sau sau vòng lặp.

.TrimEnd(',')

hoặc đơn giản là thay đổi thành

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

4
Anh ta đang sử dụng StringBuilder không phải chuỗi. Hơn nữa, nó khá không hiệu quả: chuyển đổi đầu tiên thành chuỗi sau đó cắt tỉa.
Piotr Stapp

hoặcstring.Join(",", input)
Tvde1

10

Còn cái này thì sao..

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

7

Tôi thích thao tác độ dài của trình tạo chuỗi:

data.Length = data.Length - 1;

4
Tại sao không đơn giản data.Length--hoặc --data.Length?
Gone Coding

Tôi thường sử dụng data.Length-- nhưng trong một trường hợp, tôi đã phải lùi lại 2 ký tự do giá trị trống sau ký tự tôi muốn xóa. Trim cũng không hoạt động trong trường hợp đó data.Length = data.Length - 2; đã làm việc.
Caverman

Lợi nhuận Trim một thể hiện mới của một chuỗi, nó không làm thay đổi nội dung của đối tượng StringBuilder
bastos.sergio

@GoneCoding Visual Basic .NET không hỗ trợ --hoặc ++ Bạn có thể sử dụng data.Length -= 1mặc dù, hoặc câu trả lời này cũng sẽ hoạt động.
Jason S

3

Tôi khuyên bạn nên thay đổi thuật toán vòng lặp của mình:

  • Thêm dấu phẩy không phải SAU mục đó mà là TRƯỚC
  • Sử dụng một biến boolean, bắt đầu bằng false, bỏ dấu phẩy đầu tiên
  • Đặt biến boolean này thành true sau khi kiểm tra nó

2
Đây có lẽ là gợi ý kém hiệu quả nhất trong số các đề xuất (và yêu cầu nhiều mã hơn).
Gone Coding

1
Nhìn vào câu trả lời @Vlad
Noctis

3

Bạn nên sử dụng string.Joinphương pháp để biến một tập hợp các mục thành một chuỗi được phân tách bằng dấu phẩy. Nó sẽ đảm bảo rằng không có dấu phẩy ở đầu hoặc cuối, cũng như đảm bảo chuỗi được xây dựng hiệu quả (không có chuỗi trung gian không cần thiết).


2

Có, hãy chuyển đổi nó thành một chuỗi sau khi hoàn tất vòng lặp:

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

3
nó khá không hiệu quả: chuyển đổi đầu tiên thành chuỗi sau đó cắt bớt.
Piotr Stapp

2
@Garath Nếu bạn muốn nói "không hiệu quả", tôi sẽ không đồng ý. Nhưng nó sẽ hiệu quả.
DonBoitnott

2

Bạn có hai lựa chọn. Đầu tiên là một Removephương pháp rất dễ sử dụng nó khá hiệu quả. Cách thứ hai là sử dụng ToStringvới chỉ mục bắt đầu và chỉ mục kết thúc ( tài liệu MSDN )



1

Cách đơn giản nhất là sử dụng phương thức Join ():

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

Nếu bạn thực sự cần StringBuilder, hãy cắt bỏ dấu phẩy kết thúc sau vòng lặp:

data.ToString().TrimEnd(',');

4
data.ToString().TrimEnd(',');không hiệu quả
bastos.sergio

1
Ngoài ra, bạn có thể không muốn chuyển đổi đối tượng StringBuilder để String vì nó có thể có nhiều dòng kết thúc bằng ""
Fandango68
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.