CSV-files 的强类型解析
Strongly typed parsing of CSV-files
因此,在绝望地抓了大约一个小时之后,我决定听从这里每个人的建议,不实施我自己的 CSV-parser.
所以我选择了 FileHelpers。
但是我在正确使用它时遇到了一些麻烦。
我的 CSV-file 看起来像这样:
50382018,50319368,eBusiness Manager,IT02,3350_FIB4,IT,2480
50370383,50373053,CRM Manager,IT01,3200_FIB3,xyz,2480
50320067,50341107,"VP, Business Information Officer",IT03,3200_FI89,xyz,2480
50299061,50350088,Project Expert,IT02,8118_FI09,abc,2480
我对 FileHelpers(特别是 CsvEngine
)的需求在第 3 行 - 注意用引号引起来的第三列,因为它有一个内部逗号(否则用作分隔符)。
我读取文件的代码是这样的:
var co = new FileHelpers.Options.CsvOptions("Employee", columnDeliminator, 7);
var ce = new CsvEngine(co);
var records = ce.ReadFile(pathToCSVFile);
它工作正常 - 有点。它正确地解析行并识别带有分隔符的值。
但是
ReadFile()
-方法的return值为object[]
。它的内容似乎是某种动态类型。
它看起来像这样 - 其中列命名为 "Field_1"、"Field_2" 等
我创建了一个 "data class" 用于保存解析后的行 它看起来像这样:
public class Employee
{
public string DepartmentPosition;
public string ParentDepartmentPosition;
public string JobTitle;
public string Role;
public string Location;
public string NameLocation;
public string EmployeeStatus;
}
有没有办法让 FileHelpers 的 CsvEngine
class 到 return 强类型数据?
如果我可以使用 FileHelpers 的 "basic" 解析器,我可以使用此代码:
var engine = new FileHelperEngine<Employee>();
var records = engine.ReadFile("Input.txt");
有没有办法让我的 "Employee" class 有 CsvEngine
return 个实例?或者我是否必须编写自己的映射代码来支持它?
documentation 以一种简单的方式为我工作:
首先在您的 class 中,它需要几个装饰器:
编辑 使用 FieldQuoted 装饰器解析引号中的任何内容并忽略包含的逗号
[DelimitedRecord(",")]
class Person
{
[FieldQuoted]
public string Name { get; set; }
[FieldConverter(ConverterKind.Int32)]
public int Age { get; set; }
public string State { get; set; }
}
DelimitedRecord
用于 class 和预期的分隔符(如果以后发生变化,这可能是个问题。
和 FieldConverter,因为它显示的不是字符串。
那就稍微改变一下阅读方式:
var fhr = new FileHelperEngine<Person>();
var readLines = fhr.ReadFile(pathToFile);
然后它就可以工作了,强类型:
foreach(var person in readLines)
{
Console.WriteLine(person.Name);
}
如果这个库不行,你也可以尝试使用内置的.Net CSV解析器TextFieldParser。例如:https://coding.abel.nu/2012/06/built-in-net-csv-parser/
已添加:
对于类型(自动转换):
static void run()
{
// split with any lib line of CSV
string[] line = new string[]{"john", "doe", "201"};
// needed prop names of class
string[] propNames = "fname|lname|room".Split('|');
Person p = new Person();
parseLine<Person>(p, line, propNames);
}
static void parseLine<T>(T t, string[] line, string[] propNames)
{
for(int i = 0;i<propNames.Length;i++)
{
string sprop = propNames[i];
PropertyInfo prop = t.GetType().GetProperty(sprop);
object val = Convert.ChangeType(line[i], prop.PropertyType);
prop.SetValue(t, val );
}
}
class Person
{
public string fname{get;set;}
public string lname{get;set;}
public int room {get;set;}
}
使用 CsvHelper 作为可行的替代方案并假设 CSV 文件没有 headers、
可以为 Employee
class 类
创建一个映射
public sealed class EmployeeClassMap : ClassMap<Employee> {
public EmployeeClassMap() {
Map(_ => _.Location).Index(0);
Map(_ => _.NameLocation).Index(1);
Map(_ => _.JobTitle).Index(2);
//...removed for brevity
}
}
其中索引映射到强类型 object 模型上的相应 属性。
要使用此映射,您需要在配置中注册映射。
using (var textReader = new StreamReader(pathToCSVFile)) {
var csv = new CsvReader(textReader);
csv.Configuration.RegisterClassMap<EmployeeClassMap>();
var records = csv.GetRecords<Employee>();
//...
}
@shamp00 有正确答案 - 我也在 FileHelper escape delimiter 找到了它。
我拿了我的模型 class 并按照建议在上面装饰了每个 属性:
(我可能不需要装饰所有属性,但现在可以)
[DelimitedRecord((","))]
public class Employee
{
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string DepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string ParentDepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string JobTitle;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Role;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Location;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string NameLocation;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string EmployeeStatus;
}
现在我只需要这段代码:
TextReader reader = new StreamReader(contents);
var engine = new FileHelperEngine<Employee>()
{
Options = { IgnoreFirstLines = 1 }
};
var myRecords = engine.ReadStream(reader);
因此,在绝望地抓了大约一个小时之后,我决定听从这里每个人的建议,不实施我自己的 CSV-parser.
所以我选择了 FileHelpers。
但是我在正确使用它时遇到了一些麻烦。
我的 CSV-file 看起来像这样:
50382018,50319368,eBusiness Manager,IT02,3350_FIB4,IT,2480
50370383,50373053,CRM Manager,IT01,3200_FIB3,xyz,2480
50320067,50341107,"VP, Business Information Officer",IT03,3200_FI89,xyz,2480
50299061,50350088,Project Expert,IT02,8118_FI09,abc,2480
我对 FileHelpers(特别是 CsvEngine
)的需求在第 3 行 - 注意用引号引起来的第三列,因为它有一个内部逗号(否则用作分隔符)。
我读取文件的代码是这样的:
var co = new FileHelpers.Options.CsvOptions("Employee", columnDeliminator, 7);
var ce = new CsvEngine(co);
var records = ce.ReadFile(pathToCSVFile);
它工作正常 - 有点。它正确地解析行并识别带有分隔符的值。
但是
ReadFile()
-方法的return值为object[]
。它的内容似乎是某种动态类型。
它看起来像这样 - 其中列命名为 "Field_1"、"Field_2" 等
我创建了一个 "data class" 用于保存解析后的行 它看起来像这样:
public class Employee
{
public string DepartmentPosition;
public string ParentDepartmentPosition;
public string JobTitle;
public string Role;
public string Location;
public string NameLocation;
public string EmployeeStatus;
}
有没有办法让 FileHelpers 的 CsvEngine
class 到 return 强类型数据?
如果我可以使用 FileHelpers 的 "basic" 解析器,我可以使用此代码:
var engine = new FileHelperEngine<Employee>();
var records = engine.ReadFile("Input.txt");
有没有办法让我的 "Employee" class 有 CsvEngine
return 个实例?或者我是否必须编写自己的映射代码来支持它?
documentation 以一种简单的方式为我工作:
首先在您的 class 中,它需要几个装饰器:
编辑 使用 FieldQuoted 装饰器解析引号中的任何内容并忽略包含的逗号
[DelimitedRecord(",")]
class Person
{
[FieldQuoted]
public string Name { get; set; }
[FieldConverter(ConverterKind.Int32)]
public int Age { get; set; }
public string State { get; set; }
}
DelimitedRecord
用于 class 和预期的分隔符(如果以后发生变化,这可能是个问题。
和 FieldConverter,因为它显示的不是字符串。
那就稍微改变一下阅读方式:
var fhr = new FileHelperEngine<Person>();
var readLines = fhr.ReadFile(pathToFile);
然后它就可以工作了,强类型:
foreach(var person in readLines)
{
Console.WriteLine(person.Name);
}
如果这个库不行,你也可以尝试使用内置的.Net CSV解析器TextFieldParser。例如:https://coding.abel.nu/2012/06/built-in-net-csv-parser/
已添加: 对于类型(自动转换):
static void run()
{
// split with any lib line of CSV
string[] line = new string[]{"john", "doe", "201"};
// needed prop names of class
string[] propNames = "fname|lname|room".Split('|');
Person p = new Person();
parseLine<Person>(p, line, propNames);
}
static void parseLine<T>(T t, string[] line, string[] propNames)
{
for(int i = 0;i<propNames.Length;i++)
{
string sprop = propNames[i];
PropertyInfo prop = t.GetType().GetProperty(sprop);
object val = Convert.ChangeType(line[i], prop.PropertyType);
prop.SetValue(t, val );
}
}
class Person
{
public string fname{get;set;}
public string lname{get;set;}
public int room {get;set;}
}
使用 CsvHelper 作为可行的替代方案并假设 CSV 文件没有 headers、
可以为 Employee
class 类
public sealed class EmployeeClassMap : ClassMap<Employee> {
public EmployeeClassMap() {
Map(_ => _.Location).Index(0);
Map(_ => _.NameLocation).Index(1);
Map(_ => _.JobTitle).Index(2);
//...removed for brevity
}
}
其中索引映射到强类型 object 模型上的相应 属性。
要使用此映射,您需要在配置中注册映射。
using (var textReader = new StreamReader(pathToCSVFile)) {
var csv = new CsvReader(textReader);
csv.Configuration.RegisterClassMap<EmployeeClassMap>();
var records = csv.GetRecords<Employee>();
//...
}
@shamp00 有正确答案 - 我也在 FileHelper escape delimiter 找到了它。
我拿了我的模型 class 并按照建议在上面装饰了每个 属性:
(我可能不需要装饰所有属性,但现在可以)
[DelimitedRecord((","))]
public class Employee
{
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string DepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string ParentDepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string JobTitle;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Role;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Location;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string NameLocation;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string EmployeeStatus;
}
现在我只需要这段代码:
TextReader reader = new StreamReader(contents);
var engine = new FileHelperEngine<Employee>()
{
Options = { IgnoreFirstLines = 1 }
};
var myRecords = engine.ReadStream(reader);