用C#parsingC#中的CSV文件
有没有一个默认的/官方/推荐的方式来parsingC#中的CSV文件? 我不想推出我自己的parsing器。
另外,我也看到有人使用ODBC / OLE DB通过Text驱动来读取CSV文件,很多人因为“缺点”而不喜欢这个。 这些缺点是什么?
理想情况下,我正在寻找一种方法,我可以通过列名读取CSV,使用第一个logging作为标题/字段名称。 一些给出的答案是正确的,但基本反序列化文件到类。
让图书馆为您处理所有的细节问题! 🙂
检查FileHelpers和保持干爽 – 不要重复自己 – 没有必要重新发明轮子gazillionth时间….
你基本上只需要通过一个公共类(像默认值,NULL值replace等精心devise的属性)来定义数据的形状 – CSV中各行的字段,点在一个文件中的FileHelpers引擎,以及宾果 – 你从这个文件中取回所有的条目。 一个简单的操作 – 伟大的performance!
CSVparsing器现在是.NET Framework的一部分。
添加对Microsoft.VisualBasic.dll的引用(在C#中正常工作,不介意名称)
using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv")) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); while (!parser.EndOfData) { //Process row string[] fields = parser.ReadFields(); foreach (string field in fields) { //TODO: Process field } } }
文档在这里 – TextFieldParser类
CsvHelper会将CSV文件读取到自定义对象中。
var csv = new CsvReader( File.OpenText( "file.csv" ) ); var myCustomObjects = csv.GetRecords<MyCustomObject>();
有时你不拥有你想要读取的对象。 在这种情况下,你可以使用stream畅的映射,因为你不能在类上放置属性。
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>(); } }
在商业应用程序中,我使用codeproject.com, CSVReader上的开源项目。
运行良好,性能良好。 在我提供的链接上有一些基准。
一个简单的例子,从项目页面复制:
using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true)) { int fieldCount = csv.FieldCount; string[] headers = csv.GetFieldHeaders(); while (csv.ReadNextRecord()) { for (int i = 0; i < fieldCount; i++) Console.Write(string.Format("{0} = {1};", headers[i], csv[i])); Console.WriteLine(); } }
正如你所看到的,这很容易处理。
我知道它有点晚了,但只是find了一个库Microsoft.VisualBasic.FileIO
它有TextFieldParser
类来处理CSV文件。
如果你只需要阅读CSV文件,那么我推荐这个库: 一个快速的CSV阅读器
如果您还需要生成csv文件,然后使用这一个: FileHelpers
他们都是免费的,开源的。
这里是我经常使用的助手类,以防有人回到这个线程(我想分享它)。
我使用这个简单的将它移植到可以使用的项目中:
public class CSVHelper : List<string[]> { protected string csv = string.Empty; protected string separator = ","; public CSVHelper(string csv, string separator = "\",\"") { this.csv = csv; this.separator = separator; foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s))) { string[] values = Regex.Split(line, separator); for (int i = 0; i < values.Length; i++) { //Trim values values[i] = values[i].Trim('\"'); } this.Add(values); } } }
并使用它:
public List<Person> GetPeople(string csvContent) { List<Person> people = new List<Person>(); CSVHelper csv = new CSVHelper(csvContent); foreach(string[] line in csv) { Person person = new Person(); person.Name = line[0]; person.TelephoneNo = line[1]; people.Add(person); } return people; }
[更新的csv帮手:修正了最后一个换行符创build一个新行的错误]
我已经写了.NET的TinyCsvParser ,它是最快的.NETparsing器之一,可以高度configuration来parsing几乎所有的CSV格式。
它在MIT许可下发布:
你可以使用NuGet来安装它。 在程序包pipe理器控制台中运行以下命令。
PM> Install-Package TinyCsvParser
用法
想象一下,我们有CSV文件persons.csv
中的人名单,其名字,姓氏和出生date。
FirstName;LastName;BirthDate Philipp;Wagner;1986/05/12 Max;Musterman;2014/01/02
我们的系统中相应的域模型可能如下所示。
private class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } }
使用TinyCsvParser时,您必须定义CSV数据中的列与您的域模型中的属性之间的映射。
private class CsvPersonMapping : CsvMapping<Person> { public CsvPersonMapping() : base() { MapProperty(0, x => x.FirstName); MapProperty(1, x => x.LastName); MapProperty(2, x => x.BirthDate); } }
然后我们可以使用映射来parsingCsvParser
的CSV数据。
namespace TinyCsvParser.Test { [TestFixture] public class TinyCsvParserTest { [Test] public void TinyCsvTest() { CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' }); CsvPersonMapping csvMapper = new CsvPersonMapping(); CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper); var result = csvParser .ReadFromFile(@"persons.csv", Encoding.ASCII) .ToList(); Assert.AreEqual(2, result.Count); Assert.IsTrue(result.All(x => x.IsValid)); Assert.AreEqual("Philipp", result[0].Result.FirstName); Assert.AreEqual("Wagner", result[0].Result.LastName); Assert.AreEqual(1986, result[0].Result.BirthDate.Year); Assert.AreEqual(5, result[0].Result.BirthDate.Month); Assert.AreEqual(12, result[0].Result.BirthDate.Day); Assert.AreEqual("Max", result[1].Result.FirstName); Assert.AreEqual("Mustermann", result[1].Result.LastName); Assert.AreEqual(2014, result[1].Result.BirthDate.Year); Assert.AreEqual(1, result[1].Result.BirthDate.Month); Assert.AreEqual(1, result[1].Result.BirthDate.Day); } } }
用户指南
完整的用户指南可在以下url获得:
此解决scheme使用官方的Microsoft.VisualBasic程序集来parsingCSV。
优点:
- 分隔符转义
- 忽略标题
- 修剪空间
- 忽略评论
码:
using Microsoft.VisualBasic.FileIO; public static List<List<string>> ParseCSV (string csv) { List<List<string>> result = new List<List<string>>(); // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) { parser.CommentTokens = new string[] { "#" }; parser.SetDelimiters(new string[] { ";" }); parser.HasFieldsEnclosedInQuotes = true; // Skip over header line. //parser.ReadLine(); while (!parser.EndOfData) { var values = new List<string>(); var readFields = parser.ReadFields(); if (readFields != null) values.AddRange(readFields); result.Add(values); } } return result; }
我正在寻找一个非常快速的解决scheme,不愿意添加额外的依赖关系。 由于我发现的不是我想做的最佳select,所以我写了自己的。 随意使用它。
更新代码在这里: https : //gist.github.com/mariodivece/9614872
没有官方的方式我知道,但你应该确实使用现有的库。 这里是我发现从CodeProject真正有用的一个:
基于如何正确使用C#split()函数分割CSV文件的不受限制的post? :
string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");
注:这不会处理转义/嵌套逗号等,因此只适用于某些简单的CSV列表。
这是我的KISS实施…
using System; using System.Collections.Generic; using System.Text; class CsvParser { public static List<string> Parse(string line) { const char escapeChar = '"'; const char splitChar = ','; bool inEscape = false; bool priorEscape = false; List<string> result = new List<string>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < line.Length; i++) { char c = line[i]; switch (c) { case escapeChar: if (!inEscape) inEscape = true; else { if (!priorEscape) { if (i + 1 < line.Length && line[i + 1] == escapeChar) priorEscape = true; else inEscape = false; } else { sb.Append(c); priorEscape = false; } } break; case splitChar: if (inEscape) //if in escape sb.Append(c); else { result.Add(sb.ToString()); sb.Length = 0; } break; default: sb.Append(c); break; } } if (sb.Length > 0) result.Add(sb.ToString()); return result; } }
前段时间我写了基于Microsoft.VisualBasic
库的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.Rows[0]
单元格:)
这段代码读取csv到DataTable:
public static DataTable ReadCsv(string path) { DataTable result = new DataTable("SomeData"); using (TextFieldParser parser = new TextFieldParser(path)) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); bool isFirstRow = true; //IList<string> headers = new List<string>(); while (!parser.EndOfData) { string[] fields = parser.ReadFields(); if (isFirstRow) { foreach (string field in fields) { result.Columns.Add(new DataColumn(field, typeof(string))); } isFirstRow = false; } else { int i = 0; DataRow row = result.NewRow(); foreach (string field in fields) { row[i++] = field; } result.Rows.Add(row); } } } return result; }
另一个列表, Cinchoo ETL – 一个用于读写多种文件格式(CSV,平面文件,Xml,JSON等)的开源库,
请在CodeProject上查看如何使用它的文章。