使用C#解析CSV文件

时间:2015-03-26 18:46:20

标签: c# parsing csv

我在C#中使用VB的TextField来解析CSV文件。但是当它到达“

时我得到一个错误
using (TextFieldParser csvReader = new TextFieldParser(csvFilePath)) {
    csvReader.SetDelimiters(new string[] { "," });
    csvReader.HasFieldsEnclosedInQuotes = true;
    string[] colFields = csvReader.ReadFields();
    foreach (string column in colFields)
    {
        DataColumn datacolumn = new DataColumn(column);
        datacolumn.AllowDBNull = true;
        csvData.Columns.Add(datacolumn);
    }
    while (!csvReader.EndOfData)
    {
        string[] fieldData = csvReader.ReadFields();
        for (int i = 0; i < fieldData.Length; i++)
        {
            if (fieldData[i] == "")
            {
                fieldData[i] = null;
            }
        }
        csvData.Rows.Add(fieldData);
    }
}

这是导致错误的csv中的行:

"101","Brake System","Level should be between \"MIN\" and \"MAX\" marks."

我不知道如何使用TextFieldParser

来处理C#

3 个答案:

答案 0 :(得分:2)

如果csv文件适合内存,你可以读入它,用\"替换每个"",并使用MemoryStream作为TextFieldParser的输入:

string data = File.ReadAllText(@"C:\temp\csvdata.txt").Replace("\\\"", "\"\"");

//TODO: Use the correct Encoding.
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(data)))
{
    using (TextFieldParser csvReader = new TextFieldParser(ms))
    {
        csvReader.SetDelimiters(new string[] { "," });
        csvReader.HasFieldsEnclosedInQuotes = true;
        string[] colFields = csvReader.ReadFields();
        foreach (string s in colFields)
        {
            Console.WriteLine(s);
        }
    }
}

对于您的示例数据,输出

  

101
  制动系统
  等级应介于&#34; MIN&#34;和&#34; MAX&#34;标记。

答案 1 :(得分:0)

以下是使用两种不同方法而不使用TextFieldParser的方法。 TextFieldParser非常慢,不建议在实际的生产应用程序中使用。

以下是仅使用String方法的简单方法,并假设它使用,分隔,没有任何引号或任何其他特殊的CSV格式。

FileInfo file = new FileInfo("myfile.csv");
using (TextReader reader = file.OpenText())
{
    for(String line = reader.ReadLine(); line != null; line = reader.ReadLine())
    {
         string[] fields = line.Split(new[] {','});
         foreach(String f in fields)
         {
             //do whatever you need for each field
          }
     }
 }

现在,如果您想使用CsvHelper(在nuget上可用),那么您有一个更复杂的CSV文件,其中包含引号字段,标题或者CSV行可以直接映射到您的对象那么这个图书馆可能对你有帮助。

未映射示例:

FileInfo file = new FileInfo("myfile.csv");
using (TextReader reader = file.OpenText())
using (CsvReader csv = new CsvReader(reader))
{
     csv.Configuration.Delimiter = ",";
     csv.Configuration.HasHeaderRecord = false;
     csv.Configuration.IgnoreQuotes = true; //if you don't use field quoting
     csv.Configuration.TrimFields = true; //trim fields as you read them
     csv.Configuration.WillThrowOnMissingField = false; //otherwise null fields aren't allowed

     while(csv.Read())
     {
          myStringVar = csv.GetField<string>(0); //gets first field as string
          myIntVar = csv.GetField<int>(1); //gets second field as int
          ... //etc, you get the idea

     }
}

映射示例:

映射类 - 假设您有一个名为MyClass的类,其字段名为field1,field2,field3

public sealed class MyClassMap : CsvClassMap<MyClass>
{
     public MyClassMap()
     {
         Map(m => m.field1).Index(0);
         Map(m => m.field2).Index(1);
         Map(m => m.field3).Index(2);
     }
 }

解析代码

FileInfo file = new FileInfo("myfile.csv");
using (TextReader reader = file.OpenText())
using (CsvReader csv = new CsvReader(reader))
{
     csv.Configuration.Delimiter = ",";
     csv.Configuration.HasHeaderRecord = false;
     csv.Configuration.IgnoreQuotes = true; //if you don't use field quoting
     csv.Configuration.TrimFields = true; //trim fields as you read them
     csv.Configuration.WillThrowOnMissingField = false; //otherwise null fields aren't allowed
     csv.Configuration.RegisterClassMap<MyClassMap>(); //adds our mapping class to the reader

     while(csv.Read())
     {
         myObject = csv.GetRecord<MyClass>();
         //do whatever here
     }
}

这两种方法都不会在乎你的csv文件中有\这样的奇怪字符。

免责声明:我与CsvHelper没有任何关系,但过去在一些项目中取得了成功,使我的生活变得更加轻松

答案 2 :(得分:0)

如果您不介意使用其他库,Ctl.Data有一种模式(parseMidQuotes: true)专门用于解析像这样的损坏的CSV。

using (StreamReader sr = new StreamReader("data.csv"))
{
    var reader = new CsvReader<Record>(sr, parseMidQuotes: true, readHeader: false);

    while (reader.Read())
    {
        Record rec = reader.CurrentObject.Value;
        rec.Description = rec.Description.Replace("\\\"", "\"");

        // use record...
    }
}

定义您的Record对象:

(通常它会将文件的标题与属性相匹配,但在您使用无标题文件的情况下,您需要使用Column属性指定顺序。

class Record
{
    [Column(Order = 0)]
    public int Id { get; set; }

    [Column(Order = 1)]
    public string Category { get; set; }

    [Column(Order = 2)]
    public string Description { get; set; }
}

(免责声明:我是该图书馆的作者)