Лучший способ удалить последний символ из строки, созданной с помощью конструктора строк


92

У меня есть следующие

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

Проблема в том, что я использую его в цикле, и там будет пробная запятая. Как лучше всего убрать запятую в конце?

Должен ли я преобразовать данные в строку или ее подстроку?


10
string.Join(",", yourCollection)? Изменить: добавлено в качестве ответа.
Влад


@Chris: таким образом вам вообще не нужен StringBuilder.
Влад

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

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

Ответы:


224

Самый простой и эффективный способ - выполнить эту команду:

data.Length--;

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

data.Length = 0;

опять же, потому что это не меняет таблицу распределения. Думайте об этом как о том, что я не хочу больше узнавать эти байты. Теперь, даже при звонке ToString(), он не узнает ничего, кроме своего Length, ну, не может. Это изменяемый объект, который выделяет больше места, чем вы ему предоставляете, он просто построен таким образом.


2
re data.Length = 0;: именно это и StringBuilder.Clearработает, поэтому StringBuilder.Clearдля ясности намерений лучше использовать .
Eren Ersönmez

@ ErenErsönmez, честный друг, я должен был более четко сказать, что это то, что Clear()делает, но забавная вещь. Это первая строка из Clear()метода. Но знаете ли вы, что интерфейс на самом деле выдает файл return this;. Вот что меня убивает. Устанавливая Length = 0изменения, ссылка у вас уже есть, зачем возвращать самому?
Mike Perrenoud

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

43

Просто используйте

string.Join(",", yourCollection)

Таким образом, вам не понадобится StringBuilderцикл и.




Длинное добавление об async case. По состоянию на 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
@Vlad, я понимаю, только двойная проверка, поскольку я делаю простой тест, например, raw test, и они не дали того же. string.Join(",", yourCollection)все еще есть ,в конце. так что приведенное выше ie string.Join(",", yourCollection)неэффективно и не удаляет его самостоятельно.
Seabizkit

2
@Seabizkit: Очень странно! Не могли бы вы опубликовать пример? В моем коде он отлично работает: ideone.com/aj8PWR
Vlad

2
Годы использования цикла для создания построителя строк с последующим удалением конечной запятой, и я мог бы просто использовать это. Спасибо за чаевые!
Caverman

11

Используйте следующее после цикла.

.TrimEnd(',')

или просто измените на

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

4
Он использует StringBuilder, а не строку. Более того, это совершенно неэффективно: сначала преобразование в строку, затем обрезка.
Петр Стапп

илиstring.Join(",", input)
Tvde1 06

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?
Gone Coding

Обычно я использую data.Length, но в одном случае мне пришлось вернуться на 2 символа из-за пустого значения после символа, который я хотел удалить. Обрезка и в этом случае не работала, поэтому data.Length = data.Length - 2; работал.
Caverman

Trim возвращает новый экземпляр строки, он не изменяет содержимое объекта
построителя

@GoneCoding Visual Basic .NET не поддерживает --или ++ вы можете использовать data.Length -= 1, или этот ответ тоже будет работать.
Джейсон С.

3

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

  • Ставьте запятую не ПОСЛЕ пункта, а ПЕРЕД
  • Используйте логическую переменную, которая начинается с false, подавите первую запятую
  • Установите для этой логической переменной значение true после ее тестирования

2
Это, вероятно, наименее эффективное из всех предложений (и требует больше кода).
Gone Coding

1
Посмотрите ответ @Vlad
Ноктис

3

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


2

Да, преобразовать его в строку после завершения цикла:

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

3
это совершенно неэффективно: сначала преобразование в строку, затем обрезка.
Петр Стапп

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.