将对象列表转换为 csv 的最快方法,每个对象值都在新行中
本文关键字:对象 新行中 方法 转换 列表 csv | 更新日期: 2023-09-27 18:20:49
>我有一个类如下:
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; }
}
我有一个测试对象列表
List<Test>testobjs=new List();
现在我想将其转换为以下格式的csv:
"1,约翰·格里沙姆,2014/9/5,2014/9/5,1356,0'2,斯蒂芬·金,2014/9/3,2014/9/9,1367,0'3,造雨者,4/9/2014,18/9/2014,1";
我搜索了"将列表转换为csv c#",得到了如下解决方案:
string.Join(",", list.Select(n => n.ToString()).ToArray())
但这不会根据需要放置 ',即针对每个对象
除了字符串构建之外,还有什么最快的方法可以做到这一点吗?请帮忙...
使用 servicestack.text
Install-Package ServiceStack.Text
然后使用字符串扩展方法ToCsv(T)/FromCsv()
例子:https://github.com/ServiceStack/ServiceStack.Text
更新: Servicestack.Text
现在在曾经是商业的 v4 中也是免费的。无需再指定版本!祝你连载愉快!
因为问题中提到了速度,所以我的兴趣被激起了相对性能可能是什么,以及我能以多快的速度获得它。
我知道StringBuilder被排除在外,但它仍然感觉可能是最快的,StreamWriter当然具有写入MemoryStream或直接写入文件的优势,这使得它具有多功能性。
所以我敲了一个快速测试。
我建立了一个列表,列出了与您的对象相同的五十万个对象。
然后我用CsvSerializer和两个手工卷紧版本进行序列化,一个使用StreamWriter到MemoryStream,另一个使用StringBuilder。
手工滚动的代码被编码以应对引号,但没有更复杂的东西。 这段代码非常紧凑,我可以管理的中间字符串最少,没有串联...... 但不是生产,当然也没有风格或灵活性。
但所有三种方法的输出都是相同的。
时间很有趣:
序列化五十万个对象,每个方法运行五次,所有时间都到最接近的整数毫秒:
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
这是在具有大量RAM的高端i7上。
在其他条件相同的情况下,我会一直使用图书馆。
但是,如果 2:1 的性能差异变得至关重要,或者如果 RAM 或其他问题被证明夸大了较大数据集上的差异,或者如果数据以块的形式到达并直接发送到磁盘,我可能会受到诱惑......
以防万一有人感兴趣,代码的核心(对于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);
}
如果你不想加载库,你可以创建以下方法:
private void SaveToCsv<T>(List<T> reportData, string path)
{
var lines = new List<string>();
IEnumerable<PropertyDescriptor> props = TypeDescriptor.GetProperties(typeof(T)).OfType<PropertyDescriptor>();
var header = string.Join(",", props.ToList().Select(x => x.Name));
lines.Add(header);
var valueLines = reportData.Select(row => string.Join(",", header.Split(',').Select(a => row.GetType().GetProperty(a).GetValue(row, null))));
lines.AddRange(valueLines);
File.WriteAllLines(path, lines.ToArray());
}
然后调用该方法:
SaveToCsv(testobjs, "C:/PathYouLike/FileYouLike.csv")
最好的选择是使用现有的库。它为您省去了自己弄清楚的麻烦,并且可能会处理转义的特殊字符,添加标题行等。您可以使用ServiceStack中的CSVSerializer。但是nuget中还有其他几个。然后,创建CSV将像string csv = CsvSerializer.SerializeToCsv(testobjs);
一样简单
FileHelpers
库将List
的对象转换为CSV。
考虑给定的对象,向其添加DelimitedRecord
Attribute
。
[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:回答为时已晚,但希望这对某人有所帮助。
使用 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
样品小提琴:https://dotnetfiddle.net/M7v7Hi
稍微死灵一下; 遇到了与上面完全相同的场景,走上了使用 FastMember
的道路,所以我们不必每次向类添加属性时都调整代码:
[HttpGet]
public FileResult GetCSVOfList()
{
// Get your list
IEnumerable<MyObject> myObjects =_service.GetMyObject();
//Get the type properties
var myObjectType = TypeAccessor.Create(typeof(MyObject));
var myObjectProperties = myObjectType.GetMembers().Select(x => x.Name);
//Set the first row as your property names
var csvFile = string.Join(',', myObjectProperties);
foreach(var myObject in myObjects)
{
// Use ObjectAccessor in order to maintain column parity
var currentMyObject = ObjectAccessor.Create(myObject);
var csvRow = Environment.NewLine;
foreach (var myObjectProperty in myObjectProperties)
{
csvRow += $"{currentMyObject[myObjectProperty]},";
}
csvRow.TrimEnd(',');
csvFile += csvRow;
}
return File(Encoding.ASCII.GetBytes(csvFile), "text/csv", "MyObjects.csv");
}
应生成一个CSV
,第一行是字段的名称,行紧随其后。现在。。。在 CSV 中读取并将其创建回对象列表...
注意:示例在 ASP.NET 核心MVC中,但应该与.NET框架非常相似。也考虑过ServiceStack.Text
但许可证不容易遵循。
我真的很喜欢@marks答案。添加该版本,添加对值中的转义","的支持以及更多注释
/// <summary>
/// Saves a list of data objects to a CSV file.
/// </summary>
/// <typeparam name="T">The type of the data objects.</typeparam>
/// <param name="reportData">The list of data objects.</param>
/// <returns>A string representation of the CSV file.</returns>
private string SaveToCsv<T>(List<T> reportData)
{
var lines = new List<string>();
// Get the properties of the data type
var props = TypeDescriptor.GetProperties(typeof(T)).OfType<PropertyDescriptor>();
// Create the header line by joining the property names
var header = string.Join(",", props.Select(property => property.Name));
lines.Add(header);
// Create value lines for each data object
var valueLines = reportData.Select(row =>
{
// Get the property values and enclose them in quotes if they contain a comma
var values = header.Split(',').Select(propertyName =>
{
var propertyValue = row.GetType().GetProperty(propertyName)?.GetValue(row, null);
var valueString = propertyValue?.ToString();
// Add quotes if the value contains a comma
if (valueString?.Contains(',') == true)
valueString = $"'"{valueString}'"";
return valueString;
});
// Join the values with commas
var line = string.Join(",", values);
return line;
});
// Add the value lines to the result
lines.AddRange(valueLines);
// Join all lines with newline characters
return string.Join("'n", lines);
}
如果您使用的是较小的代码:
private string SaveToCsv<T>(List<T> reportData)
{
var props = TypeDescriptor.GetProperties(typeof(T)).OfType<PropertyDescriptor>();
var header = string.Join(",", props.Select(property => property.Name));
var lines = new List<string> { header };
lines.AddRange(reportData.Select(row => string.Join(",", props.Select(property =>
{
var value = property.GetValue(row);
return value != null && value.ToString().Contains(",") ? $"'"{value}'"" : value?.ToString();
}))));
return string.Join("'n", lines);
}
LINQtoCSV 是我发现的最快、最轻的,可以在 GitHub 上找到。允许您通过属性属性指定选项。
有关最佳解决方案,您可以阅读本文:将对象列表转换为 CSV 文件 C# - Codingvila
using Codingvila.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace Codingvila.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
CodingvilaEntities entities = new CodingvilaEntities();
var lstStudents = (from Student in entities.Students
select Student);
return View(lstStudents);
}
[HttpPost]
public FileResult ExportToCSV()
{
#region Get list of Students from Database
CodingvilaEntities entities = new CodingvilaEntities();
List<object> lstStudents = (from Student in entities.Students.ToList()
select new[] { Student.RollNo.ToString(),
Student.EnrollmentNo,
Student.Name,
Student.Branch,
Student.University
}).ToList<object>();
#endregion
#region Create Name of Columns
var names = typeof(Student).GetProperties()
.Select(property => property.Name)
.ToArray();
lstStudents.Insert(0, names.Where(x => x != names[0]).ToArray());
#endregion
#region Generate CSV
StringBuilder sb = new StringBuilder();
foreach (var item in lstStudents)
{
string[] arrStudents = (string[])item;
foreach (var data in arrStudents)
{
//Append data with comma(,) separator.
sb.Append(data + ',');
}
//Append new line character.
sb.Append("'r'n");
}
#endregion
#region Download CSV
return File(Encoding.ASCII.GetBytes(sb.ToString()), "text/csv", "Students.csv");
#endregion
}
}
}