Самый быстрый способ преобразования списка объектов в 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: слишком поздно, чтобы ответить, но надеюсь, что это кому-нибудь поможет.