【问题标题】:CsvHelper - Read different record types in same CSVCsvHelper - 在同一个 CSV 中读取不同的记录类型
【发布时间】:2016-07-21 22:13:51
【问题描述】:

我正在尝试从具有以下结构的 CSV 文件中读取两种类型的记录:

PlaceName,Longitude,Latitude,Elevation
NameString,123.456,56.78,40

Date,Count
1/1/2012,1
2/1/2012,3
3/1/2012,10
4/2/2012,6

我知道这个问题之前已经在

但是当我运行我的实现时,它得到一个CsvMissingFieldExceptionFields 'Date' do not exist in the CSV file。我有两个定义和地图类,一个用于位置,另一个用于计数,它们是:

public class LocationDefinition
{
    public string PlaceName { get; set; }
    public double Longitude { get; set; }
    public double Latitude { get; set; }
    public double Elevation { get; set; }
}

public sealed class LocationMap : CsvClassMap<LocationDefinition>
{
    public LocationMap()
    {
        Map(m => m.PlaceName).Name("PlaceName");
        Map(m => m.Longitude).Name("Longitude");
        Map(m => m.Latitude).Name("Latitude");
        Map(m => m.Elevation).Name("Elevation");
    }            
}     

public class CountDefinition
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public sealed class CountMap : CsvClassMap<CountDefinition>
{
    public CountMap()
    {
        Map(m => m.Date).Name("Date");
        Map(m => m.Count).Name("Count");
    }
}

我用于读取 csv 文件的代码是:

LocationDefinition Location;
var Counts = new List<CountDefinition>();

using (TextReader fileReader = File.OpenText(@"Path\To\CsvFile"))
using (var csvReader = new CsvReader(fileReader))
{
    csvReader.Configuration.RegisterClassMap<LocationMap>();
    csvReader.Configuration.RegisterClassMap<CountMap>();

    // Only reads a single line of Location data
    csvReader.Read();
    LocationData = csvReader.GetRecord<LocationDefinition>();
    csvReader.Read(); // skip blank line
    csvReader.Read(); // skip second header section

    // Read count data records
    while (csvReader.Read())
    {
        var tempCount = csvReader.GetRecord<CountDefinition>();
        Counts.Add(tempCount);
    }
}

tempCount 行上抛出异常。据我所知,它仍然需要位置记录,但我原以为GetRecord&lt;CountDefinition&gt; 会指定记录类型。我也试过ClearRecordCache 和注销LocationMap 无济于事。

应如何更改此代码以使其读取此结构的 csv 文件?

【问题讨论】:

  • 结构是否重复?您可以发布多个结构。 40 年来一直在解析这样的文件,并且可以轻松给出一个很好的解决方案。
  • location部分只出现一次,会在count部分之前(目前只有一条记录),count部分可以有任意条记录。让代码能够处理任意数量的位置记录可能是值得的。
  • 我会在出现 CRLF 的地方将文件一分为二。一旦它初始化了 Location def,我就无法放手。

标签: c# csv csvhelper


【解决方案1】:

试试这个

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

namespace ConsoleApplication1
{
    enum State
    {
        FIND_RECORD,
        GET_LOCATION,
        GET_DATES
    }
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            StreamReader reader = new StreamReader(FILENAME);
            State state = State.FIND_RECORD;

            LocationDefinition location = null;
            string inputLine = "";
            while ((inputLine = reader.ReadLine()) != null)
            {
                inputLine = inputLine.Trim();
                if (inputLine.Length == 0)
                {
                    state = State.FIND_RECORD;
                }
                else
                {
                    switch (state)
                    {
                        case State.FIND_RECORD :
                            if (inputLine.StartsWith("PlaceName"))
                            {
                                state = State.GET_LOCATION;
                            }
                            else
                            {
                                if (inputLine.StartsWith("Date"))
                                {
                                    state = State.GET_DATES;
                                }
                            }
                            break;
                        case State.GET_DATES :
                            if (location.dates == null) location.dates = new CountDefinition();
                            location.dates.dates.Add(new CountDefinition(inputLine));
                            break;
                        case State.GET_LOCATION :
                            location = new LocationDefinition(inputLine);
                            break;
                    }
                }
            }
        }
    }
    public class LocationDefinition
    {
        public static List<LocationDefinition> locations = new List<LocationDefinition>();
        public CountDefinition dates { get; set; }
        public string PlaceName { get; set; }
        public double Longitude { get; set; }
        public double Latitude { get; set; }
        public double Elevation { get; set; }

        public LocationDefinition(string location)
        {
            string[] array = location.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            PlaceName = array[0];
            Longitude = double.Parse(array[1]);
            Latitude = double.Parse(array[2]);
            Elevation = double.Parse(array[3]);
            locations.Add(this);
         }
    }



    public class CountDefinition
    {
        public List<CountDefinition> dates = new List<CountDefinition>();
        public DateTime Date { get; set; }
        public int Count { get; set; }

        public CountDefinition() { ;}

        public CountDefinition(string count)
        {
            string[] array = count.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            Date = DateTime.Parse(array[0]);
            Count = int.Parse(array[1]);
            dates.Add(this);
        }
    }


}

【讨论】:

  • 我希望能修复我的 CsvHelper 代码,尽管这对于想要避免使用外部库的任何人都很有用。
  • 如果使用字符串拆分功能可以轻松读取文本文件,为什么有人会使用 3rd 方应用程序来简单地读取文本文件?
【解决方案2】:

我在问题跟踪器上收到了 Josh Close 的回复:

CsvReader not recognising different registered class maps

这是他对这个问题的回答:

由于您没有单个标题,因此您需要忽略标题 并改用索引。不过,这提出了一个想法。我本可以有 ReadHeader 方法解析特定记录类型的标头。

下面是一个对你有用的例子。

void Main()
{
    LocationDefinition Location;
    var Counts = new List<CountDefinition>();

    using (var stream = new MemoryStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream))
    using (var csvReader = new CsvReader(reader))
    {
        writer.WriteLine("PlaceName,Longitude,Latitude,Elevation");
        writer.WriteLine("NameString,123.456,56.78,40");
        writer.WriteLine();
        writer.WriteLine("Date,Count");
        writer.WriteLine("1/1/2012,1");
        writer.WriteLine("2/1/2012,3");
        writer.WriteLine("3/1/2012,10");
        writer.WriteLine("4/2/2012,6");
        writer.Flush();
        stream.Position = 0;

        csvReader.Configuration.HasHeaderRecord = false;
        csvReader.Configuration.RegisterClassMap<LocationMap>();
        csvReader.Configuration.RegisterClassMap<CountMap>();

        csvReader.Read(); // get header
        csvReader.Read(); // get first record
        var locationData = csvReader.GetRecord<LocationDefinition>();

        csvReader.Read(); // skip blank line
        csvReader.Read(); // skip second header section

        // Read count data records
        while (csvReader.Read())
        {
            var tempCount = csvReader.GetRecord<CountDefinition>();         
            Counts.Add(tempCount);
        }
    }
}

public class LocationDefinition
{
    public string PlaceName { get; set; }
    public double Longitude { get; set; }
    public double Latitude { get; set; }
    public double Elevation { get; set; }
}

public sealed class LocationMap : CsvClassMap<LocationDefinition>
{
    public LocationMap()
    {
        Map(m => m.PlaceName);
        Map(m => m.Longitude);
        Map(m => m.Latitude);
        Map(m => m.Elevation);
    }
}

public class CountDefinition
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public sealed class CountMap : CsvClassMap<CountDefinition>
{
    public CountMap()
    {
        Map(m => m.Date);
        Map(m => m.Count);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 1970-01-01
    相关资源
    最近更新 更多