CSV文件的强类型解析

时间:2018-11-11 16:46:41

标签: c# csv text-parsing filehelpers

因此,在绝望地拉了一个小时之后,我决定听从这里的每个人的建议,而 不要实施我自己的CSV解析器

所以我改为使用FileHelpers

但是我在正确使用它时遇到了一些麻烦。

我的CSV文件看起来像这样:

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()方法的返回值为object[]。而且它的内容似乎是一种动态类型。

看起来像这样-列名为“ Field_1”,“ Field_2”等。

Automatically generated return type

我创建了一个“数据类”,用于保存已解析的行,它看起来像这样:

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类来返回强类型数据?

如果我只能使用FileHelpers的“基本”解析器,则可以使用以下代码:

var engine = new FileHelperEngine<Employee>();
var records = engine.ReadFile("Input.txt");

是否可以让CsvEngine返回“ Employee”类的实例?还是我必须编写自己的映射代码来支持此操作?

4 个答案:

答案 0 :(得分:0)

documentation通过一种简单的方式为我工作:

首先在课堂上,它需要几个装饰器:

编辑:使用FieldQuoted装饰器来解析引号中的所有内容,并忽略包含的逗号

[DelimitedRecord(",")]
class Person
{
    [FieldQuoted]
    public string Name { get; set; }

    [FieldConverter(ConverterKind.Int32)]
    public int Age { get; set; }

    public string State { get; set; }
}

DelimitedRecord用于类和预期的分隔符(如果以后发生更改,可能会出现问题。

和它的FieldConverter出现,除了字符串以外。

然后稍微改变您的阅读方式:

var fhr = new FileHelperEngine<Person>();            
var readLines = fhr.ReadFile(pathToFile);

然后它可以工作,强类型输入:

foreach(var person in readLines)
{
   Console.WriteLine(person.Name);
}

答案 1 :(得分:0)

使用CsvHelper作为可行的替代方法,并假定CSV文件没有标题,

可以为Employee类创建映射,例如

public sealed class EmployeeClassMap : ClassMap<Employee> {
    public EmployeeClassMap() {
        Map(_ => _.Location).Index(0);
        Map(_ => _.NameLocation).Index(1);
        Map(_ => _.JobTitle).Index(2);
        //...removed for brevity
    }
}

将索引映射到强类型对象模型上的相应属性的地方。

要使用此映射,您需要在配置中注册该映射。

using (var textReader = new StreamReader(pathToCSVFile)) {
    var csv = new CsvReader(textReader);
    csv.Configuration.RegisterClassMap<EmployeeClassMap>();

    var records = csv.GetRecords<Employee>();

    //...
}

答案 2 :(得分:0)

@ shamp00的答案正确-我也在FileHelper escape delimiter上找到了它。

我上了模型课,并按照建议装饰了每个属性:

(我可能不需要装饰所有属性,但现在可以使用)

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "segue" {
    let nextVC = segue.destination as! UINavigationController
    let dest = nextVC.topViewController as! ViewController
    let ind = sender as! customTableViewCell

    let nTxt = nameTxt.text?.description
    let pTxt = phoneTxt.text?.description
    let eTxt = emailTxt.text?.description
    let dTxt = dobTxt.text?.description
    ind.lb1 = "\(nTxt!)"
    ind.lb2 = "\(pTxt!)"
    ind.lb3 = "\(eTxt!)"
    ind.lb4 = "\(dTxt!)"
    }
}

现在我只需要以下代码:

[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;
}

答案 3 :(得分:-1)

如果该库不起作用,您还可以尝试使用内置的.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;}
    }