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);