Создайте строки с разделителями-запятыми в C#

У меня есть объект, который содержит много значений, и некоторые из них (не все значения из объекта) нужно поместить в CSV. Мой подход был таким:

string csvString = o.number + "," + o.id + "," + o.whatever ....

Есть ли лучший, более элегантный способ?


person grady    schedule 03.02.2011    source источник
comment
Вообще говоря, объединение строк с символом + считается менее эффективным. Он создает больше объектов для сборки мусора.   -  person Phil Gan    schedule 03.02.2011


Ответы (5)


Если вы поместите все свои значения в массив, по крайней мере, вы можете использовать string.Join.

string[] myValues = new string[] { ... };
string csvString = string.Join(",", myValues);

Вы также можете использовать перегрузку string.Join, которая принимает params string в качестве второго параметра, например:

string csvString = string.Join(",", value1, value2, value3, ...);
person Øyvind Bråthen    schedule 03.02.2011
comment
+1 за использование массива между ними, что делает код более разборчивым, когда значений больше нескольких. - person Bazzz; 03.02.2011
comment
@Bazzz - Да, это может быстро запутаться, если вы введете более 10 параметров в вызов метода соединения. Я думаю, что первый подход является самым чистым, но для нескольких значений второй также подходит. - person Øyvind Bråthen; 03.02.2011
comment
Почему два отрицательных голоса по этому ответу? Будет оценена причина, чтобы все знали, в чем, по вашему мнению, проблема с этим ответом. - person Øyvind Bråthen; 03.02.2011
comment
Да, один был от меня (извините, лол). У него нет своих значений в массиве, и просто нет необходимости использовать массив в качестве промежуточного шага для построения строки. Это неэффективно и делает код длиннее (и не более читаемым). Я предпочитаю просто использовать Join напрямую (как ваше обновление). - person fearofawhackplanet; 03.02.2011
comment
@fearofawhackplanet - Спасибо, что указали причину. Так больше смысла ;) Вы, конечно, имеете право на собственное мнение. я просто люблю это слушать ;) - person Øyvind Bråthen; 03.02.2011
comment
+1 за использование массива; это аккуратнее, и функциональность может быть легко расширена. - person Phil Gan; 03.02.2011
comment
@fear: string.Join использует массив параметров. Это означает, что код, который передает параметры напрямую, будет автоматически преобразован компилятором для использования массива. С точки зрения накладных расходов разницы между вызовами нет. - person Jeff Mercado; 03.02.2011
comment
Это не совсем правильный ответ для строк CSV. True CSV будет экранировать строки по мере необходимости. - person jgmjgm; 28.09.2015
comment
Что делать, если значение содержит запятую, например. У меня есть такие значения, как test1, test2 и test, test3, где test, test3 следует рассматривать как одно значение. - person Nimesh khatri; 06.05.2021

Другой подход заключается в использовании класса CommaDelimitedStringCollection из пространства имен/сборки System.Configuration. Он ведет себя как список, плюс у него есть переопределенный метод ToString, который возвращает строку, разделенную запятыми.

Плюсы: более гибкий, чем массив.

Минусы - Вы не можете передать строку, содержащую запятую.

CommaDelimitedStringCollection list = new CommaDelimitedStringCollection();

list.AddRange(new string[] { "Huey", "Dewey" });
list.Add("Louie");
//list.Add(",");

string s = list.ToString(); //Huey,Dewey,Louie
person grysik44    schedule 03.02.2011

Вы можете использовать метод string.Join, чтобы сделать что-то вроде string.Join(",", o.Number, o.Id, o.whatever, ...) .

edit: Как сказал digEmAll, string.Join быстрее, чем StringBuilder. Они используют внешнюю реализацию для string.Join.

Код профилирования (конечно, выполняется в релизе без символов отладки):

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        string r;
        int iter = 10000;

        string[] values = { "a", "b", "c", "d", "a little bit longer please", "one more time" };

        sw.Restart();
        for (int i = 0; i < iter; i++)
            r = Program.StringJoin(",", values);
        sw.Stop();
        Console.WriteLine("string.Join ({0} times): {1}ms", iter, sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < iter; i++)
            r = Program.StringBuilderAppend(",", values);
        sw.Stop();
        Console.WriteLine("StringBuilder.Append ({0} times): {1}ms", iter, sw.ElapsedMilliseconds);
        Console.ReadLine();
    }

    static string StringJoin(string seperator, params string[] values)
    {
        return string.Join(seperator, values);
    }

    static string StringBuilderAppend(string seperator, params string[] values)
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(values[0]);
        for (int i = 1; i < values.Length; i++)
        {
            builder.Append(seperator);
            builder.Append(values[i]);
        }
        return builder.ToString();
    }
}

string.Join занял 2 мс на моей машине, а StringBuilder.Append 5 мс. Так что есть заметная разница. Спасибо digAmAll за подсказку.

person jb_    schedule 03.02.2011
comment
string.Join так же быстро, как StringBuilder (если не немного быстрее), потому что они оба выделяют только одну строку - person digEmAll; 03.02.2011
comment
Проблема с обычной строкой concat (например, с использованием string1 += string2) заключается в том, что исходная строка1 отбрасывается (поскольку строки неизменяемы), а новая сумма строк1 и строка2 указывается строкой1, и это не очень эффективно, если делал неоднократно. Однако, как указывает digEmAll, string.Join, конечно, выделяет строку только один раз. Не один раз для каждого элемента в массиве. - person Øyvind Bråthen; 03.02.2011
comment
Одним из недостатков string.Join (до .NET 4.0) является то, что для него требуется массив строк, что вынуждает вас выделять его, если у вас есть только общий IEnumerable<string>... в любом случае, .NET 4.0 решил эту проблему. - person digEmAll; 03.02.2011
comment
Вы правы, я отредактировал свой пост. Но мне интересно, какой внешний метод вызывается для string.Join, поскольку это собственная реализация, а не внутри mscorlib? - person jb_; 03.02.2011
comment
@jb_: на самом деле (моя ошибка) я проверил только реализацию .net 2 string.Join, и эти 2 перегрузки (взяв string[]) используют внешний метод с именем FastAllocateString, который (возможно, иногда) немного быстрее. Удивительно (для меня), новые перегрузки string.Join, представленные в .net 4, просто используют StringBuilder внутри! Таким образом, в основном нет никакой разницы между двумя методами, поэтому я предлагаю использовать string.Join() для простоты (и для принципа не изобретать колесо) - person digEmAll; 04.02.2011
comment
@digEmAll: Но основы по-прежнему неизменны, так что оправдания не нужны :-). Перегрузки string.Join(string, string[]) и string.Join(string, string[], int, int) по-прежнему используют FastAllocateString и, следовательно, могут работать быстрее, чем другие перегрузки string.Join. Но я согласен с предпочтительным использованием string.Join вместо реализации собственной логики соединения с помощью StringBuilder. - person jb_; 04.02.2011

Если вы используете .NET 4, вы можете использовать перегрузку для string.Join, которая также принимает IEnumerable, если они у вас есть в списке:

string.Join(", ", strings);
person Jackson Pope    schedule 03.02.2011

Вы можете переопределить метод ToString() вашего объекта:

public override string ToString ()
{
    return string.Format ("{0},{1},{2}", this.number, this.id, this.whatever);
}
person Dimitris Tavlikos    schedule 03.02.2011
comment
Однако это полезно только в том случае, если он точно знает количество элементов, которые он хочет поместить в строку. string.Join, вероятно, лучше подходит. - person Øyvind Bråthen; 03.02.2011
comment
Да, но поскольку он хочет создать CSV-файл, элементы, скорее всего, каждый раз будут одинаковыми. Не знаю, это вопрос предпочтений. - person Dimitris Tavlikos; 03.02.2011
comment
Из вопроса я предположил, что ему всегда нужны одни и те же поля, поэтому он определенно знает количество элементов. В этом ответе нет ничего плохого, хотя мне не очень нравится переопределять здесь ToString, поскольку он может быть недостаточно универсальным. +1 от меня в любом случае. - person fearofawhackplanet; 03.02.2011
comment
@fearofawhackplanet - В этом нет ничего плохого. Просто вопрос предпочтений ;) - person Øyvind Bråthen; 03.02.2011