【问题标题】:Read CSV to list of objects [duplicate]读取 CSV 到对象列表 [重复]
【发布时间】:2015-01-03 14:37:21
【问题描述】:

我有一个 CSV 文件,其中列出了各种数据(日期时间、十进制)。来自 CSV 的示例行:

Date,Open,High,Low,Close,Volume,Adj Close  //I need to skip this first line as well
2012-11-01,77.60,78.12,77.37,78.05,186200,78.05

我有一个创建的对象列表,我想将每一行读入其中。对象的构造函数如下,每个 CSV 行中的每个字段都在此处使用和分配。

    public DailyValues(DateTime date, decimal open, decimal high, decimal low,
        decimal close, decimal volume, decimal adjClose)
        : this()
    {
        Date = date;
        Open = open;
        High = high;
        Low = low;
        Close = close;
        Volume = volume;
        AdjClose = adjClose;
    }

    List<DailyValues> values = new List<DailyValues>();

有没有一种简单的方法可以将 CSV 的每一行读入我的列表 values 并适当地分配每个属性(即日期、开放、高)?

【问题讨论】:

  • TextFieldParser 类隐藏在 Microsoft.VisualBasic.FileIO 命名空间中。即使它说 VisualBasic,你也可以将它与 C# 一起使用。
  • 我会通过 Linq 执行此操作 - 请参阅此帖子:stackoverflow.com/questions/3497699/csv-to-object-model-mapping
  • @Kevin 这不完全符合 CSV 规范。如果数据中存在任何包含逗号的数据,则会出现问题
  • @cost - 我完全同意,但查看示例数据我认为没有问题。

标签: c# csv


【解决方案1】:

为什么不直接解析这些?你的属性数量有限,所以这不是很困难。我没有使用需要许多参数的构造函数,而是使用了一个静态方法,该方法返回一个新的DailyValues 实例作为它的返回类型。这类似于DateTime.FromBinary 等。

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;

namespace CsvDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            List<DailyValues> values = File.ReadAllLines("C:\\Users\\Josh\\Sample.csv")
                                           .Skip(1)
                                           .Select(v => DailyValues.FromCsv(v))
                                           .ToList();
        }
    }

    class DailyValues
    {
        DateTime Date;
        decimal Open;
        decimal High;
        decimal Low;
        decimal Close;
        decimal Volume;
        decimal AdjClose;

        public static DailyValues FromCsv(string csvLine)
        {
            string[] values = csvLine.Split(',');
            DailyValues dailyValues = new DailyValues();
            dailyValues.Date = Convert.ToDateTime(values[0]);
            dailyValues.Open = Convert.ToDecimal(values[1]);
            dailyValues.High = Convert.ToDecimal(values[2]);
            dailyValues.Low = Convert.ToDecimal(values[3]);
            dailyValues.Close = Convert.ToDecimal(values[4]);
            dailyValues.Volume = Convert.ToDecimal(values[5]);
            dailyValues.AdjClose = Convert.ToDecimal(values[6]);
            return dailyValues;
        }
    }
}

当然,您仍然可以添加默认构造函数,并且您需要添加异常处理以防解析失败(您也可以使用TryParse)。

  • File.ReadAllLines 将 CSV 文件中的所有行读取到字符串数组中。
  • .Skip(1) 跳过标题行。
  • .Select(v =&gt; DailyValues.FromCsv(v)) 使用 Linq 选择每一行并使用 FromCsv 方法创建一个新的 DailyValues 实例。这将创建一个 System.Collections.Generic.IEnumerable&lt;CsvDemo.DailyValues&gt; 类型。
  • 最后,.ToList() 将 IEnumerable 转换为 List 以匹配您想要的类型。

您可以简单地使用foreach 循环将每个DailyValues 实例添加到您的列表中,而不是使用Linq。

【讨论】:

  • 我喜欢这种方法,如果需要,您可以在 FromCsv 中实现更复杂的 CSV 解析。 +1
  • 这比 stackoverflow 上提供的任何其他解决方案都简单得多:)
  • 简单有效! +1
  • 虽然没有缺陷,但如果实际数据中有逗号怎么办,例如“洛杉矶,CA”将分成 2 个单独的字段。好吧,如果你知道数据不包含字符串。
  • @Paul:当然,但大多数实现都有不同的标准来处理逗号/特殊字符转义(即,一种方法是将包含逗号的值用双引号括起来,如你所说)。因此,您最好在已知情况下修改这些情况的逻辑,或者改用处理 CSV 解析的库。我的回答假设问题中描述的最简单的情况。
猜你喜欢
  • 1970-01-01
  • 2013-04-10
  • 2015-01-09
  • 1970-01-01
  • 2017-10-16
  • 1970-01-01
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多