Самый быстрый способ преобразования списка объектов в csv с каждым значением объекта в новой строке

У меня есть класс следующим образом:

public class Test
{
    public int Id {get;set;}
    public string Name { get; set; }
    public string CreatedDate {get;set;}
    public string DueDate { get; set; } 
    public string ReferenceNo { get; set; }       
    public string Parent { get; set; }
}

и у меня есть список объектов Test

List<Test>testobjs=new List();

Теперь я хотел бы преобразовать его в csv в следующем формате:

"1, Джон Гришам, 9/5/2014,9/5/2014,1356,0\n2, Стивен Кинг, 9/3/2014,9/9/2014,1367,0\n3, The Rainmaker, 4/9/2014,18/9/2014,1";

Я искал "Преобразование списка в csv С#", и я получил решения следующим образом:

string.Join(",", list.Select(n => n.ToString()).ToArray())

Но это не поместит \n по мере необходимости i.e для каждого объекта

Есть ли какой-нибудь быстрый способ, кроме строкового построения, для этого? Пожалуйста, помогите...

Ответ 1

Используйте servicestack.text

Install-Package ServiceStack.Text

а затем использовать методы расширения строки ToCsv(T)/FromCsv()

Примеры: https://github.com/ServiceStack/ServiceStack.Text

Обновление: Servicestack.Text теперь бесплатно и в версии 4, которая раньше была коммерческой. Не нужно указывать версию больше! Удачного сериализации!

Ответ 2

Поскольку скорость была упомянута в вопросе, мой интерес был подчёркнурован в отношении того, что может относиться к относительным действиям, и насколько быстро я смог его получить.

Я знаю, что StringBuilder был исключен, но он по-прежнему чувствовал себя, вероятно, самым быстрым, и StreamWriter имеет, конечно же, преимущество записи либо в MemoryStream, либо непосредственно в файл, что делает его универсальным.

Итак, я быстро провел тест.

Я создал список полмиллиона объектов, идентичных вашим.

Затем я сериализуюсь с помощью CsvSerializer и с двумя ручными жесткими версиями, используя StreamWriter для MemoryStream, а другой - с помощью StringBuilder.

Код ручного проката был закодирован, чтобы справиться с кавычками, но не более сложным. Этот код был довольно плотным, с минимальным я мог справиться с промежуточными строками, без конкатенации... но не с производством и, конечно же, с точки зрения стиля и гибкости.

Но результат был идентичным во всех трех методах.

Интересны тайминги:

Сериализуя полмиллиона объектов, пять прогонов с каждым методом, все время до ближайшего целого mS:

StringBuilder     703     734     828     671     718   Avge=     730.8
MemoryStream      812     937     874     890     906   Avge=     883.8
CsvSerializer   1,734   1,469   1,719   1,593   1,578   Avge=   1,618.6

Это было на высоком конце i7 с большим количеством ОЗУ.

При прочих равных условиях я всегда буду использовать библиотеку.

Но если разница в производительности 2: 1 стала критической или если ОЗУ или другие проблемы оказались преувеличенными для разницы в более крупном наборе данных или если данные поступали в куски и должны были быть отправлены прямо на диск, я мог бы просто соблазняйтесь...

На всякий случай, кого интересует, ядро ​​кода (для версии StringBuilder) было

    private void writeProperty(StringBuilder sb, string value, bool first, bool last)
    {
        if (! value.Contains('\"'))
        {
            if (!first)
                sb.Append(',');

            sb.Append(value);

            if (last)
                sb.AppendLine();
        }
        else
        {
            if (!first)
                sb.Append(",\"");
            else
                sb.Append('\"');

            sb.Append(value.Replace("\"", "\"\""));

            if (last)
                sb.AppendLine("\"");
            else
                sb.Append('\"');
        }
    }

    private void writeItem(StringBuilder sb, Test item)
    {
        writeProperty(sb, item.Id.ToString(), true, false);
        writeProperty(sb, item.Name, false, false);
        writeProperty(sb, item.CreatedDate, false, false);
        writeProperty(sb, item.DueDate, false, false);
        writeProperty(sb, item.ReferenceNo, false, false);
        writeProperty(sb, item.Parent, false, true);
    }

Ответ 3

Ваш лучший вариант - использовать существующую библиотеку. Это избавляет вас от необходимости разбираться в себе, и это, вероятно, будет связано с ускользанием специальных символов, добавлением заголовков и т.д. Вы можете использовать CSVSerializer из ServiceStack. Но есть несколько других в nuget. Создание CSV будет таким же простым, как string csv = CsvSerializer.SerializeToCsv(testobjs);

Ответ 4

Используйте Cinchoo ETL

Install-Package ChoETL

или же

Install-Package ChoETL.NETStandard

Пример показывает, как его использовать

List<Test> list = new List<Test>();

list.Add(new Test { Id = 1, Name = "Tom" });
list.Add(new Test { Id = 2, Name = "Mark" });

using (var w = new ChoCSVWriter<Test>(Console.Out)
    .WithFirstLineHeader()
    )
{
    w.Write(list);
}

Выходной CSV:

Id,Name,CreatedDate,DueDate,ReferenceNo,Parent
1,Tom,,,,
2,Mark,,,,

Для получения дополнительной информации, перейдите на GitHub

https://github.com/Cinchoo/ChoETL

Ответ 5

LINQtoCSV - самый быстрый и легкий из всех, что я нашел, и он доступен на GitHub. Позволяет указать параметры через атрибуты свойства.

Ответ 6

Вы можете использовать библиотеку FileHelpers для преобразования списка объектов в CSV.

Рассмотрите данный объект, добавьте к нему атрибут DelimitedRecord.

[DelimitedRecord(",")]
public class Test
{
    public int Id {get;set;}
    public string Name { get; set; }
    public string CreatedDate {get;set;}
    public string DueDate { get; set; } 
    public string ReferenceNo { get; set; }       
    public string Parent { get; set; }
 }

После того, как список заполнен, (в соответствии с вопросом это testobjs)

var engine = new FileHelperEngine<Test>();
engine.HeaderText = engine.GetFileHeader();
string dirPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + ConfigurationManager.AppSettings["MyPath"];
if (!Directory.Exists(dirPath))
{
   Directory.CreateDirectory(dirPath);
}

//File location, where the .csv goes and gets stored.
string filePath = Path.Combine(dirPath, "MyTestFile_" + ".csv");
engine.WriteFile(filePath, testobjs);

Это просто сделает работу за вас. Я использовал это для генерации отчетов о данных некоторое время, пока я не переключился на Python.

PS: слишком поздно, чтобы ответить, но надеюсь, что это кому-нибудь поможет.