在c#中是否有默认/官方/推荐的方法来解析CSV文件?我不想滚动自己的解析器。

另外,我也见过人们使用ODBC/OLE DB通过文本驱动程序读取CSV的实例,很多人因为它的“缺点”而不鼓励这样做。这些缺点是什么?

理想情况下,我正在寻找一种方法,通过它我可以通过列名读取CSV,使用第一个记录作为报头/字段名。给出的一些答案是正确的,但基本上是将文件反序列化为类。


当前回答

CsvHelper(我维护的一个库)将把CSV文件读入自定义对象。

using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
    var records = csv.GetRecords<Foo>();
}

有时候你并不拥有你想读的对象。在这种情况下,您可以使用流畅映射,因为您不能将属性放在类上。

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}

其他回答

这个列表中的另一个,Cinchoo ETL -一个开源库,可以读写多种文件格式(CSV,平面文件,Xml, JSON等)

下面的示例显示了如何快速读取CSV文件(不需要POCO对象)

string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine($"Id: {rec.Id}");
        Console.WriteLine($"Name: {rec.Name}");
    }
}

下面的示例展示了如何使用POCO对象读取CSV文件

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void CSVTest()
{
    string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

    using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
        .WithFirstLineHeader()
        )
    {
        foreach (var rec in p)
        {
            Console.WriteLine($"Id: {rec.Id}");
            Console.WriteLine($"Name: {rec.Name}");
        }
    }
}

请在CodeProject上查看如何使用它的文章。

我知道有点晚了,但刚刚找到了Microsoft.VisualBasic.FileIO库,其中有TextFieldParser类来处理csv文件。

这个解析器支持在列中嵌套逗号和引号:

static class CSVParser
{
    public static string[] ParseLine(string line)
    {
        List<string> cols = new List<string>();
        string value = null;

        for(int i = 0; i < line.Length; i++)
        {
            switch(line[i])
            {
                case ',':
                    cols.Add(value);
                    value = null;
                    if(i == line.Length - 1)
                    {// It ends with comma
                        cols.Add(null);
                    }
                    break;
                case '"':
                    cols.Add(ParseEnclosedColumn(line, ref i));
                    i++;
                    break;
                default:
                    value += line[i];
                    if (i == line.Length - 1)
                    {// Last character
                        cols.Add(value);                           
                    }
                    break;
            }
        }

        return cols.ToArray();
    }//ParseLine

    static string ParseEnclosedColumn(string line, ref int index)
    {// Example: "b"",bb"
        string value = null;
        int numberQuotes = 1;
        int index2 = index;

        for (int i = index + 1; i < line.Length; i++)
        {
            index2 = i;
            switch (line[i])
            {
                case '"':
                    numberQuotes++;
                    if (numberQuotes % 2 == 0)
                    {
                        if (i < line.Length - 1 && line[i + 1] == ',')
                        {
                            index = i;
                            return value;
                        }
                    }
                    else if (i > index + 1 && line[i - 1] == '"')
                    {
                        value += '"';
                    }
                    break;
                default:
                    value += line[i];
                    break;
            }
        }

        index = index2;
        return value;
    }//ParseEnclosedColumn 
}//class CSVParser

让一个库为你处理所有的细节!: -)

检查FileHelpers和保持干燥-不重复自己-不需要重新发明轮子的亿万次....

基本上,您只需要定义数据的形状——CSV中各个行中的字段——通过一个公共类(以及诸如默认值、NULL值替换等经过精心考虑的属性),将FileHelpers引擎指向一个文件,然后就可以从该文件中获得所有条目。一个简单的操作-卓越的性能!

CsvHelper(我维护的一个库)将把CSV文件读入自定义对象。

using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
    var records = csv.GetRecords<Foo>();
}

有时候你并不拥有你想读的对象。在这种情况下,您可以使用流畅映射,因为您不能将属性放在类上。

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}