将对象列表转换为 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())

但这不会根据需要放置 ',即针对每个对象

除了字符串构建之外,还有什么最快的方法可以做到这一点吗?请帮忙...

将对象列表转换为 csv 的最快方法,每个对象值都在新行中

使用 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
        }
    }
}