【问题标题】:Fastest way to convert a list of objects to csv with each object values in a new line将对象列表转换为 csv 的最快方法,每个对象值都在新行中
【发布时间】:2014-10-30 05:55:29
【问题描述】:

我有一堂课如下:

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,约翰·格里沙姆,9/5/2014,9/5/2014,1356,0\n2,斯蒂芬·金,9/3/2014,9/9/2014,1367,0\n3,造雨者,4/9/2014,18/9/2014,1";

我搜索了“Converting list to csv c#”,得到的解决方案如下:

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

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

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

【问题讨论】:

  • 你是否覆盖了默认的ToString
  • 不,我没有尝试覆盖..你能告诉我一个覆盖如何帮助实现这一目标的例子吗?
  • 与您的标题相反,您不是在寻找最快的方式,而是寻找任何有效的方式。 string.Join(",", ...) 不是编写 CSV 的正确方法。见Writing a CSV file in .net
  • 这与我的标题并不矛盾,我只是说这是我在SO中搜索得到的解决方案
  • 再次阅读我的评论。为什么要明确要求“最快的方式”?您不只是在寻找“任何可行的方式”吗?

标签: c# csv


【解决方案1】:

对这个有点死灵法;遇到了与上面完全相同的场景,走上了使用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 Core MVC 中,但应该与 .NET 框架非常相似。也考虑过ServiceStack.Text,但许可证并不容易遵循。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      您可以使用 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:回答为时已晚,但希望这对某人有所帮助。

      【讨论】:

        【解决方案4】:

        使用 servicestack.text

        Install-Package ServiceStack.Text
        

        然后使用字符串扩展方法ToCsv(T)/FromCsv()

        示例: https://github.com/ServiceStack/ServiceStack.Text

        更新: Servicestack.Text 现在在 v4 中也是免费的,它曾经是商业的。不再需要指定版本!祝连载愉快!

        【讨论】:

        • 感谢您处理换行符 :)
        • 那里的配额到底是多少? “JSON、JSV 和 CSV 序列化程序中的 20 种不同类型 *”是什么意思?
        • 20 个不同的类别。但如果您使用的是 v3,则没有限制。
        【解决方案5】:

        LINQtoCSV 是我发现的最快和最轻的,可在 GitHub 上找到。允许您通过属性属性指定选项。

        【讨论】:

          【解决方案6】:

          由于问题中提到了速度,我的兴趣被激起了关于相对性能可能是什么,以及我能以多快的速度获得它。

          我知道 StringBuilder 被排除在外,但它仍然感觉可能是最快的,而且 StreamWriter 当然具有写入 MemoryStream 或直接写入文件的优势,这使其用途广泛。

          所以我做了一个快速测试。

          我建立了一个与你相同的 50 万个对象的列表。

          然后我使用 CsvSerializer 进行序列化,并使用两个手动压缩版本,一个使用 StreamWriter 到 MemoryStream,另一个使用 StringBuilder。

          手卷代码被编码以处理引号,但没有更复杂的。这段代码非常紧凑,我可以管理最少的中间字符串,没有连接......但不是生产,当然也没有风格或灵活性。

          但所有三种方法的输出都是相同的。

          时间安排很有趣:

          序列化 50 万个对象,每种方法运行 5 次,一直到最接近的整数毫秒:

          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);
              }
          

          【讨论】:

          • 非常感谢您的分析。
          【解决方案7】:

          您最好的选择是使用现有的库。它为您省去了自己弄清楚的麻烦,它可能会处理转义特殊字符、添加标题行等。 您可以使用 ServiceStack 中的CSVSerializer。但nuget 中还有其他几个。 创建 CSV 将像 string csv = CsvSerializer.SerializeToCsv(testobjs);

          一样简单

          【讨论】:

          • 也感谢您推荐一个不错的库。
          猜你喜欢
          • 2020-09-18
          • 2011-03-11
          • 2019-06-09
          • 1970-01-01
          • 1970-01-01
          • 2019-03-05
          • 2017-10-07
          • 2021-02-17
          • 2011-01-19
          相关资源
          最近更新 更多