CsvHelper - 在同一 CSV 中读取不同的记录类型
本文关键字:记录 类型 读取 CSV CsvHelper | 更新日期: 2023-09-27 17:58:04
我正在尝试使用以下结构从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
我知道这个问题之前在
- 使用 CsvHelper 从单个 csv 文件中读取多个类
- 一个文件中的多种记录类型?
但是当我运行我的实现时,它会得到一个CsvMissingFieldException
说Fields '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<CountDefinition>
会指定记录类型。我也尝试ClearRecordCache
并取消注册LocationMap
无济于事。
应该如何更改此代码以使其读取此结构的csv文件?
试试这个
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);
}
}
}
我收到了Josh Close在问题跟踪器上的回复:
CsvReader 无法识别不同的已注册类映射
以下是他对这个问题的回答:
由于您没有单个标头,因此需要忽略标头 并改用索引。不过,这带来了一个想法。我本可以 方法分析特定记录类型的标头。
不过,这是一个应该适合您的示例。
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); } }