使用C#读取CSV文件

我正在编写一个简单的导入应用程序,需要读取一个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]) }); } 

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

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

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

这里是示例代码:

 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#项目中非常适合我。

以下是一些链接/信息:

  • MSDN:读取从Visual Basic中的逗号分隔文本文件
  • MSDN:TextFieldParser类

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

这些是我遇到的变种:

  • 引号引用并加倍(excel)即15“ – > field1,”15“”“,field3
  • 报价不会改变,除非该字段是由于其他原因引用。 即15“ – > field1,15”,fields3
  • 引号与\转义。 即15“ – > field1,”15 \“”,field3
  • 引号不会改变(这不总是可以正确parsing)
  • 分隔符被引用(excel)。 即a,b – > field1,“a,b”,field3
  • 分隔符用\转义。 即a,b – > field1,a \,b,field3

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

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

我build议从Nuget的CsvHelper 。

(添加对Microsoft.VisualBasic的引用只是感觉不对,它不仅丑陋,甚至可能不是跨平台的。)

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

  1. 在这个例子中,我使用StreamReader来读取文件
  2. 正则expression式来检测每行的分隔符。
  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 */ } } 

CSV可以快速复杂化。

使用一些健壮的和经过充分testing的:
FileHelpers: http://www.filehelpers.net

FileHelpers是一个免费且易于使用的.NET库,用于从文件,string或stream中的固定长度或分隔logging导入/导出数据。

我在这里使用这个:

http://www.codeproject.com/KB/database/GenericParser.aspx

上次我正在寻找这样的东西,我发现它是对这个问题的答案。

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

 using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader() .WithField('Field1') .WithField('Field2') ) { foreach (dynamic item in reader) { Console.WriteLine(item.Field1); Console.WriteLine(item.Field2); } } 

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

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

首先需要了解什么是CSV以及如何编写它。

  1. 每个下一个string( /r/n )是下一个“表”行。
  2. “表格”单元格由一些分隔符号分隔。 最经常使用的符号是\t,
  3. 每个单元格都可能包含此分隔符号(单元格必须以引号符号开始,并以此符号结束)
  4. 每个单元格可能包含/r/n sybols(在这种情况下,单元格必须以引号符号开头并以此符号结尾)

C#/ Visual Basic处理CSV文件的最简单方法是使用标准的Microsoft.VisualBasic库。 你只需要添加需要的引用,并将下面的string添加到你的类中:

 using Microsoft.VisualBasic.FileIO; 

是的,你可以在C#中使用它,不用担心。 这个库可以读取相对较大的文件并支持所有需要的规则,因此您可以使用所有的CSV文件。

前段时间我写了基于这个库的CSV读写简单类。 使用这个简单的类,你将能够像使用2维数组一样使用CSV。 您可以通过以下链接find我的课程: https : //github.com/ukushu/DataExporter

使用简单的例子:

 Csv csv = new Csv("\t");//delimiter symbol csv.FileOpen("c:\\file1.csv"); var row1Cell6Value = csv.Rows[0][5]; csv.AddRow("asdf","asdffffff","5") csv.FileSave("c:\\file2.csv"); 

要完成以前的答案,可能需要从CSV文件中获取对象的集合,或者通过TextFieldParserstring.Split方法进行分析,然后通过Reflection将每行转换为对象。 您显然首先需要定义一个与CSV文件的行匹配的类。

我使用从这里find的Michael Kropat简单的CSV序列化程序: 通用类到CSV(所有属性) ,并重用他的方法来获取希望的类的字段和属性。

我使用以下方法反序列化我的CSV文件:

 public static IEnumerable<T> ReadCsvFileTextFieldParser<T>(string fileFullPath, string delimiter = ";") where T : new() { if (!File.Exists(fileFullPath)) { return null; } var list = new List<T>(); var csvFields = GetAllFieldOfClass<T>(); var fieldDict = new Dictionary<int, MemberInfo>(); using (TextFieldParser parser = new TextFieldParser(fileFullPath)) { parser.SetDelimiters(delimiter); bool headerParsed = false; while (!parser.EndOfData) { //Processing row string[] rowFields = parser.ReadFields(); if (!headerParsed) { for (int i = 0; i < rowFields.Length; i++) { // First row shall be the header! var csvField = csvFields.Where(f => f.Name == rowFields[i]).FirstOrDefault(); if (csvField != null) { fieldDict.Add(i, csvField); } } headerParsed = true; } else { T newObj = new T(); for (int i = 0; i < rowFields.Length; i++) { var csvFied = fieldDict[i]; var record = rowFields[i]; if (csvFied is FieldInfo) { ((FieldInfo)csvFied).SetValue(newObj, record); } else if (csvFied is PropertyInfo) { var pi = (PropertyInfo)csvFied; pi.SetValue(newObj, Convert.ChangeType(record, pi.PropertyType), null); } else { throw new Exception("Unhandled case."); } } if (newObj != null) { list.Add(newObj); } } } } return list; } public static IEnumerable<MemberInfo> GetAllFieldOfClass<T>() { return from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType) let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute)) orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name select mi; } 
 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(); } } }