使用C#读取CSV文件




c# datatable (7)

CSV可以很快得到复杂化。

使用一些健壮和经过充分测试的:
FileHelpers: www.filehelpers.net

FileHelpers是一个免费且易于使用的.NET库,用于从文件,字符串或流中的固定长度或分隔记录导入/导出数据。

我正在编写一个简单的导入应用程序,需要读取CSV文件,在DataGrid显示结果并在另一个网格中显示CSV文件的损坏行。 例如,显示另一个网格中短于5个值的行。 我试图这样做:

StreamReader sr = new StreamReader(FilePath);
importingData = new Account();
string line;
string[] row = new string [5];
while ((line = sr.ReadLine()) != null)
{
    row = line.Split(',');

    importingData.Add(new Transaction
    {
        Date = DateTime.Parse(row[0]),
        Reference = row[1],
        Description = row[2],
        Amount = decimal.Parse(row[3]),
        Category = (Category)Enum.Parse(typeof(Category), row[4])
    });
}

但在这种情况下对阵列进行操作非常困难。 有没有更好的方法来分割值?


不要重新发明轮子。 利用.NET BCL中已有的功能。

  • 添加一个对Microsoft.VisualBasic的引用(是的,它说的是VisualBasic,但它也适用于C# - 记住最后它只是IL)
  • 使用Microsoft.VisualBasic.FileIO.TextFieldParser类来解析CSV文件

以下是示例代码:

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData) 
    {
        //Processing row
        string[] fields = parser.ReadFields();
        foreach (string field in fields) 
        {
            //TODO: Process field
        }
    }
}

它在我的C#项目中非常适合我。

这里有一些更多的链接/信息:


另一个名单是Cinchoo ETL - 一个用于读取和写入CSV文件的开源库

下面是一个示例CSV文件

Id, Name
1, Tom
2, Mark

很快,您可以使用库来加载它们,如下所示

using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader())
{
   foreach (dynamic item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

如果您的POCO课程与CSV文件相匹配

public class Employee
{
   public int Id { get; set; }
   public string Name { get; set; }
}

您可以使用它来加载如下的CSV文件

using (var reader = new ChoCSVReader<Employee>("test.csv").WithFirstLineHeader())
{
   foreach (var item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

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

免责声明:我是这个图书馆的作者



我的经验是,有许多不同的CSV格式。 特别是他们如何处理字段中的引号和分隔符的转义。

这些是我遇到的变体:

  • 引号引用并加倍(excel)即15“ - > field1,”15“”“,field3
  • 除非该字段因其他原因被引用,否则引号不会更改。 即15“ - > field1,15”,fields3
  • 引号用\逃脱。 即15“ - > field1,”15 \“”,field3
  • 引号根本没有改变(这并不总是可以正确解析)
  • 分隔符被引用(excel)。 即a,b - > field1,“a,b”,field3
  • 分隔符用\转义。 即a,b - > field1,a \,b,field3

我已经尝试了许多现有的csv解析器,但没有一个可以处理我遇到的变体。 从文档中发现解析器支持的转义变体也很困难。

在我的项目中,我现在使用VB TextFieldParser或自定义分割器。


有时候,当你不想重新发明轮子时,使用库很酷,但在这种情况下,与使用库相比,可以用更少的代码行来完成相同的工作,并且更易于阅读。 这是一种不同的方法,我觉得它很容易使用。

  1. 在这个例子中,我使用StreamReader来读取文件
  2. 正则表达式来检测每行的分隔符。
  3. 一个数组,用于收集索引0到n中的列
using (StreamReader reader = new StreamReader(fileName))
    {
        string line; 

        while ((line = reader.ReadLine()) != null)
        {
            //Define pattern
            Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");

            //Separating columns to array
            string[] X = CSVParser.Split(line);

            /* Do something with X */
        }
    }

private static DataTable ConvertCSVtoDataTable(string strFilePath)
        {
            DataTable dt = new DataTable();
            using (StreamReader sr = new StreamReader(strFilePath))
            {
                string[] headers = sr.ReadLine().Split(',');
                foreach (string header in headers)
                {
                    dt.Columns.Add(header);
                }
                while (!sr.EndOfStream)
                {
                    string[] rows = sr.ReadLine().Split(',');
                    DataRow dr = dt.NewRow();
                    for (int i = 0; i < headers.Length; i++)
                    {
                        dr[i] = rows[i];
                    }
                    dt.Rows.Add(dr);
                }

            }

            return dt;
        }

        private static void WriteToDb(DataTable dt)
        {
            string connectionString =
                "Data Source=localhost;" +
                "Initial Catalog=Northwind;" +
                "Integrated Security=SSPI;";

            using (SqlConnection con = new SqlConnection(connectionString))
                {
                    using (SqlCommand cmd = new SqlCommand("spInsertTest", con))
                    {
                        cmd.CommandType = CommandType.StoredProcedure;

                        cmd.Parameters.Add("@policyID", SqlDbType.Int).Value = 12;
                        cmd.Parameters.Add("@statecode", SqlDbType.VarChar).Value = "blagh2";
                        cmd.Parameters.Add("@county", SqlDbType.VarChar).Value = "blagh3";

                        con.Open();
                        cmd.ExecuteNonQuery();
                    }
                }

         }




csv