Найкращий спосіб видалити останній символ із рядка, побудованого за допомогою buildbuilder


92

У мене таке

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

Проблема з цим полягає в тому, що я використовую його в циклі, і буде випробовувана кома. Який найкращий спосіб видалити кінцеву кому?

Чи потрібно змінювати дані на рядок, що є їх підрядком?


10
string.Join(",", yourCollection)? Редагувати: додано як відповідь.
Влад


@Chris: таким чином вам взагалі не потрібен StringBuilder.
Влад

можливо, ви можете уникнути додавання коми замість того, щоб видаляти її потім. Див .: stackoverflow.com/questions/581448/… (відповідь Джона Скіта)
Паоло Фалабелла,

@Vlad Так вибачте, я неправильно прочитав це; Я думав, ви пропонуєте це як пропозицію змінити остаточно побудований рядок, а не як заміну його циклу взагалі. (Я думав, що вчасно видалив свій коментар, мабуть, ні!)
Кріс Сінклер,

Відповіді:


223

Найпростіший та найефективніший спосіб - виконати цю команду:

data.Length--;

роблячи це, ви переміщуєте покажчик (тобто останній індекс) назад на один символ, але ви не змінюєте змінність об'єкта. Насправді очищення a StringBuilderтакож найкраще зробити Length(але насправді використовуйте Clear()метод для ясності, тому що так виглядає його реалізація):

data.Length = 0;

знову ж таки, оскільки це не змінює таблицю розподілу. Подумайте про це, як про те, що я більше не хочу розпізнавати ці байти. Тепер, навіть зателефонувавши ToString(), він не розпізнає нічого минулого Length, ну, не може. Це змінний об’єкт, який виділяє більше місця, ніж те, що ви йому надаєте, він просто побудований таким чином.


2
re data.Length = 0;: це саме те, що StringBuilder.Clearробить, тому переважно використовувати StringBuilder.Clearдля ясності намірів.
Eren Ersönmez

@ ErenErsönmez, досить чесний друг, я повинен був би чіткіше сказати, що саме Clear()так, але смішно. Це перший рядок з Clear()методу. Але, чи знали ви, що інтерфейс насправді видає a return this;. Зараз це те, що мене вбиває. Встановлюючи Length = 0зміни, посилання у вас вже є, навіщо повертатись самому?
Mike Perrenoud

12
Я думаю, це для того, щоб мати можливість користуватися «вільно». Appendтакож повертається.
Eren Ersönmez

43

Просто використовуйте

string.Join(",", yourCollection)

Таким чином, вам не потрібен StringBuilderцикл and.




Довге додавання про асинхронний випадок. Станом на 2019 рік, це не рідкість, коли дані надходять асинхронно.

Якщо ваші дані знаходяться в асинхронному зборі, string.Joinперевантаження не відбувається IAsyncEnumerable<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();
        }
    }
}

Якщо дані надходять асинхронно, але інтерфейс IAsyncEnumerable<T>не підтримується (як згадано в коментарях SqlDataReader), відносно легко обернути дані в 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]);
}

і використовувати його:

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

Для використання Selectвам потрібно буде використовувати nuget-пакет System.Interactive.Async. Тут ви можете знайти приклад, який можна скласти.


12
Найкраща відповідь - не та, яка вирішує проблему, а та, яка запобігає їй.
LastTribunal

1
@Seabizkit: Звичайно! Все питання, до речі, стосується C #.
Влад

1
@ Радий, що я отримую це, лише подвійну перевірку, оскільки я роблю простий тест, такий як сирий тест, і вони не дають те саме. string.Join(",", yourCollection)все ще має в ,кінці. отже вищезазначене string.Join(",", yourCollection)є неефективним і не видаляє його самостійно.
Seabizkit,

2
@Seabizkit: Дуже дивно! Не могли б ви опублікувати приклад? У моєму коді це працює ідеально: ideone.com/aj8PWR
Влад

2
Роки використання циклу для побудови конструктора рядків, а потім видалення кінцевої коми, і я міг просто використати це. Дякуємо за підказку!
Каверман

11

Використовуйте наступне після циклу.

.TrimEnd(',')

або просто змінити на

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

4
Він використовує StringBuilder не рядок. Більше того, це досить неефективно: спочатку перетворення на рядок, а потім обрізання.
Piotr Stapp

абоstring.Join(",", input)
Tvde1

11

Як щодо цього ..

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

7

Я вважаю за краще маніпулювати довжиною конструктора рядків:

data.Length = data.Length - 1;

4
Чому не просто data.Length--чи --data.Length?
Пропало кодування

Я зазвичай використовую data.Length - але в одному випадку мені довелося повернути 2 символи назад через порожнє значення після символу, який я хотів видалити. Обрізка також не працювала в цьому випадку, тому data.Length = data.Length - 2; працював.
Каверман

Trim повертає новий екземпляр рядка, він не змінює вміст об'єкта
stringbuilder

@GoneCoding Visual Basic .NET не підтримує, --або ++ Ви можете використовувати, data.Length -= 1хоча ця відповідь також буде працювати.
Джейсон S

3

Я рекомендую вам змінити алгоритм циклу:

  • Додавайте кому не ПІСЛЯ елемента, а ДО
  • Використовуйте логічну змінну, яка починається з false, придушіть першу кому
  • Встановіть для цієї булевої змінної значення true після тестування

2
Це, мабуть, найменш ефективна з усіх пропозицій (і вимагає більше коду).
Пропало кодування

1
Подивіться на відповідь @Vlad
Noctis

3

Вам слід використовувати string.Joinметод, щоб перетворити колекцію елементів у рядок, розділений комами. Це забезпечить відсутність провідної або кінцевої коми, а також забезпечить ефективну побудову рядка (без зайвих проміжних рядків).


2

Так, перетворити його у рядок, як тільки цикл буде виконаний:

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

3
це досить неефективно: спочатку перетворення на рядок, потім обрізка.
Piotr Stapp

2
@Garath Якби ви мали на увазі "неефективний", я б не погодився. Але це було б ефективно.
DonBoitnott

2

У вас є два варіанти. По-перше, це дуже простий Removeспосіб використання, він досить ефективний. Другий спосіб - використовувати ToStringз індексом початку та кінця ( документація MSDN )



1

Найпростішим способом буде використання методу Join ():

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

Якщо вам справді потрібен StringBuilder, обріжте кінцеву кому після циклу:

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

4
data.ToString().TrimEnd(',');неефективний
bastos.sergio

1
Крім того, ви можете не захотіти перетворювати об'єкт StringBuilder в String, оскільки він може мати кілька рядків, що закінчуються на ","
Fandango68,

0

Зрозуміло !!

Більшість відповідей у ​​цій темі не будуть працювати, якщо ви використовуєте, AppendLineяк показано нижче:

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

Скрипай мене

ЧОМУ ??? @ (& ** (& @ !!

Питання просте, але мені потрібно було трохи часу, щоб це зрозуміти: оскільки в кінці є ще 2 невидимі символи CRта LF(Повернення каретки та Подача рядка). Тому потрібно забрати 3 останні символи:

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

На завершення

Використовуйте Length--або Length -= 1якщо останній метод, який ви викликали, був Append. Використовуйте, Length =- 3якщо ви останній метод, який ви викликали AppendLine.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.