繁体   English   中英

如何将csv文件读入列表<List<Object> &gt; 使用 CSVHelper

[英]How to read csv file into List<List<Object>> with CSVHelper

我在 text/csv 文件中有这些数据:

ID,Boo,Soo
0,True,qwerty
0,True,qwerty

0,True,qwerty
0,True,qwerty

0,True,qwerty
0,True,qwerty
0,True,qwerty

0,True,qwerty
0,True,qwerty
0,True,qwerty
0,True,qwerty

0,True,qwerty
0,True,qwerty
0,True,qwerty
0,True,qwerty

特别注意空行。 我想使用 NuGet 的CSVHelper将此数据读入多个列表,其中边界由空行确定。

因此,如果有一个类MyClass具有属性IDBooSoo ,并且我要直接在代码中初始化上面的示例数据,我想要一个List<List<Mycass>>像这样:

var data = {
    new List<MyClass> {
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} }
    },
    new List<MyClass> {
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} }
    },
    new List<MyClass> {
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} }
    },
    new List<MyClass> {
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} }
    },
    new List<MyClass> {
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} },
        { new MyClass {ID = 0, Boo = true, Soo = "qwerty"} }
    },
}

但是,当然,我事先不知道数据的真实情况。 我不知道每个列表需要多少个条目,也不知道需要多少个列表。 在空行之间的每个列表中可以有任意随机数量的项目。

这是我到目前为止的代码:

for (int j = 0; j < rnd.Next(4, 10); j++)
{
    for (int i = 0; i < rnd.Next(1, 7); i++)
    {
        ListMyClass.Add(new MyClass { ID = 0, Boo = true, Soo = "qwerty" });
    }

    ListListMyClass.Add(new List<MyClass>(ListMyClass));
    ListMyClass.Clear();
}

using (var csvWiter = new CsvWriter(new StreamWriter("csvHelper.csv"), CultureInfo.InvariantCulture))
{
    foreach (var listRecord in ListListMyClass)
    {
        csvWiter.WriteRecords(listRecord);
        csvWiter.NextRecord();
    }
}

//this code reads all objects, but that's wrong.
using (var reader = new StreamReader("csvHelper.csv"))
            using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
            {
                var list = csv.GetRecords<MyClass>().ToList();
                ;
            }

我怎样才能做到这一点?

编辑:我用迈克尔琼斯的解决方案解决了这个问题。 谢谢。

其他解决方案:

csvReader.Configuration.IgnoreBlankLines = false;
while (csvReader.Read())
            {
                if (csvReader.Context.Record.IsEmpty())
                {
                    ListListMyClass.Add(new List<MyClass>(ListObject));
                    ListMyClass.Clear();
                    continue;
                }
                ListMyClass.Add(csvReader.GetRecord<MyClass>());
            }

谢谢

我将添加另一个更依赖CsvHelper

public static void Main(string[] args)
{        
    using (MemoryStream stream = new MemoryStream())
    using (StreamWriter writer = new StreamWriter(stream))
    using (StreamReader reader = new StreamReader(stream))
    using (CsvReader csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        writer.WriteLine("ID,Boo,Soo");
        writer.WriteLine("0,True,\"Test\nqwerty\"");
        writer.WriteLine("0,True,qwerty");
        writer.WriteLine("");
        writer.WriteLine("0,True,qwerty");
        writer.Flush();
        stream.Position = 0;

        csv.Configuration.IgnoreBlankLines = false;

        csv.Read();
        csv.ReadHeader();

        var data = new List<List<MyClass>>();
        var resultSet = new List<MyClass>();

        while (csv.Read())
        {
            if (csv.Context.RawRecord.Trim() == string.Empty)
            {
                data.Add(resultSet);
                resultSet = new List<MyClass>();
                continue;
            }

            var record = csv.GetRecord<MyClass>();

            resultSet.Add(record);
        }
        if (resultSet.Count > 0)
        {
            data.Add(resultSet);
        }
    } 
    Console.ReadKey();
}

您可以做的是使用迭代器块来提供在空行之间中断的单独流:

public IEnumerable<TextReader> ReadSeparatedFile(string filePath)
{
     using (var rdr = new StreamReader(filePath))
     {
          string line = rdr.ReadLine();
          while(line is object)
          {
              var buffer = new StringBuilder();
              do
              {
                  if (line != string.Empty) buffer.AppendLine(line);
                  line = rdr.ReadLine();
              } while (line is object && line != string.Empty())
              yield return new StringReader(buffer.ToString());
          }
     }
}

现在您可以调用此方法并为CsvReader设置一系列流:

var data = ReadSeparatedFile("csvHelper.csv");
var result = new List<List<MyClass>>();
foreach(var stream in data)
{
    using (var rdr = new CsvReader(stream))
    {
        result.Add(rdr.GetRecords<MyClass>());
    }
}

个人不熟悉CsvReader类型如何将 csv 列映射到对象字段,您可能还想或可能不想将标题行注入每个序列。 但是我们可以通过只添加一行代码并更改另一行代码来实现:

public IEnumerable<TextReader> ReadSeparatedFile(string filePath)
{
     using (var rdr = new StreamReader(filePath))
     {
          string header = rdr.ReadLine() + "\n";
          string line = rdr.ReadLine();
          while(line is object)
          {
              var buffer = new StringBuilder(header);
              do
              {
                  if (line != string.Empty) buffer.AppendLine(line);
                  line = rdr.ReadLine();
              } while (line is object && line != string.Empty())
              yield return new StringReader(buffer.ToString());
          }
     }
}

阅读文档: https : csv.GetRecord<MyClass>();您需要调用csv.GetRecord<MyClass>(); 获取 CSV 和csv.Read()一条记录(行)以将 Reader 设置为下一行。 但是,请考虑在将来使用正确格式的文件。

老实说,我认为您不应该创建这样的 CSV 文件。 您可以使用其他格式,例如 JSON 吗? 查看Newtonsoft.Json以序列化/反序列化 JSON。 对于这样的事情,CSV 是一个糟糕的文件格式选择。

话虽如此,这将奏效,尽管感觉很丑陋。 (除非您知道如何手动设置标题,否则您必须使用 CSV 索引装饰您的模型。我没有深入研究该库)。

void Main()
{
    using (var reader = new StreamReader(@"C:\temp\csvHelper.csv"))
    {
        IEnumerable<IEnumerable<MyClass>> setOfSets = ReadCSVCollection<MyClass>(reader);
    }
}

public static IEnumerable<IEnumerable<T>> ReadCSVCollection<T>(StreamReader reader, bool hasHeader = true)
{
    StringBuilder buffer = new StringBuilder();
    string header = hasHeader ? reader.ReadLine() : null;

    while (!reader.EndOfStream)
    {
        string line = reader.ReadLine();
        if (line.Trim() == string.Empty)
        {
            yield return ReadCSVString<T>(buffer.ToString());
            buffer = new StringBuilder();
            continue;
        }
        buffer.AppendLine(line);
    }

    yield return ReadCSVString<T>(buffer.ToString());
}

public static IEnumerable<T> ReadCSVString<T>(string input, bool hasHeader = false)
{
    using (StringReader sr = new StringReader(input))
    using (CsvReader csv = new CsvReader(sr, CultureInfo.InvariantCulture))
    {
        csv.Configuration.HasHeaderRecord = hasHeader;
        return csv.GetRecords<T>().ToList();
    }
}


public class MyClass
{
    [CsvHelper.Configuration.Attributes.Index(0)]
    public int ID { get; set; }
    [CsvHelper.Configuration.Attributes.Index(1)]
    public bool Boo { get; set; }
    [CsvHelper.Configuration.Attributes.Index(2)]
    public string Soo { get; set; }
}

// Define other methods and classes here

另一种选择:使用SoftCircuits.CsvParser ,您的代码将如下所示:

List<List<MyClass>> data = new List<List<MyClass>>();

using (CsvDataReader<MyClass> reader = new CsvDataReader<MyClass>(path))
{
    reader.ReadHeaders(true);

    data.Add(new List<MyClass>());
    while (reader.Read(out MyClass item))
    {
        if (reader.ColumnCount == 0)
            data.Add(new List<MyClass>());
        else
            data[data.Count - 1].Add(item);
    }
}

我的测试表明 SoftCircuits.CsvParser 平均比 CsvHelper 快四倍。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM