本篇首先特别感谢今后启程兄的《.NetCore外国一些高质量博客分享》, 发现不少国外的.NET Core技术博客资源, 我会不按期从中选择一些有意思的文章翻译总结一下。html
本篇博客来源于.NET Core Totorials的《CSV Parsing In .NET Core》。git
对于初级程序员来讲, 使用string.Split(',')来解析CSV文件基本就是惟一可行的方法, 可是以后他们会发现除了使用逗号分隔值以外,CSV中还有其余须要处理的东西,因此做者就介绍了CSV解析的一些痛点并推荐了2个比较好用CSV解析库。程序员
这里做者推荐了2个CSV解析库,一个是CSVHelper, 一个是Tiny CSV Parser。github
为了测试这些CSV解析库,咱们首先建立一个.NET Core的控制台程序app
而后咱们添加一个Automobile类,其代码以下ide
public class Automobile { public string Make { get; set; } public string Model { get; set; } public AutomobileType Type { get; set; } public int Year { get; set; } public decimal Price { get; set; } public AutomobileComment Comment { get; set; } public override string ToString() { StringBuilder builder = new StringBuilder(); builder.AppendLine(); builder.AppendLine($"Make: {Make}"); builder.AppendLine($"Model: {Model}"); builder.AppendLine($"Type: {Type.ToString()}"); builder.AppendLine($"Year: {Year}"); builder.AppendLine($"Price: {Price}"); builder.AppendLine($"Comment: {Comment?.Comment}"); return builder.ToString(); } } public class AutomobileComment { public string Comment { get; set; } } public enum AutomobileType { None, Car, Truck, Motorbike }
最后咱们建立一个csv文件sample.txt做为测试文件,咱们但愿将当前csv文件中的数据,反序列化到一个Automobile
类的对象实例中。测试
其内容以下ui
Make,Model,Type,Year,Price,Comment "Toyota",Corolla,Car,1990,2000.99,"Comment with a, line break and "" quotes"
这个文件中第一行是一个表头,第二行是一个数据行,数据行中包含了this
CSVHelper是一个CSV文件的读写库。它支持读写自定义类对象。官网地址https://joshclose.github.io/CsvHelper/翻译
咱们可使用Package Manager Console来安装CSVHelper。
命令以下:
PM> Install-Package CsvHelper
使用CSVHelper解析CSV文件代码很简单, 还须要2步
CsvReader
类的对象实例读取CSV文件GetRecords
方法来反序列化using (TextReader reader = new StreamReader("sample.txt")) { var csvReader = new CsvReader(reader); var records = csvReader.GetRecords<Automobile>(); foreach (var r in records) { Console.WriteLine(r.ToString()); } }
最终结果
从结果上看,上面提到的CSV解析痛点,CSVHelper都实现了,特别是针对Comment字段中的逗号、换行、双引号,CSVHelper都处理的很成功。
下一个介绍的CSV解析器是Ting CSV Parser, 官网http://bytefish.github.io/TinyCsvParser/index.html, 它是使用配置的方式映射CSV字段, 使用方式上有点相似于AutoMapper
咱们可使用Package Manager Console来安装Tiny CSV Parser。
命令以下:
PM> Install-Package TinyCsvParser
使用Tiny CSV Parser解析CSV文件,首先咱们须要建立一个映射类。映射类须要继承自CsvMapping
映射类代码
public class CsvAutomobileMapping : CsvMapping<Automobile> { public CsvAutomobileMapping() : base() { MapProperty(0, x => x.Make); MapProperty(1, x => x.Model); MapProperty(2, x => x.Type, new EnumConverter<AutomobileType>()); MapProperty(3, x => x.Year); MapProperty(4, x => x.Price); MapProperty(5, x => x.Comment, new AutomobileCommentTypeConverter()); } } public class AutomobileCommentTypeConverter : ITypeConverter<AutomobileComment> { public Type TargetType => typeof(AutomobileComment); public bool TryConvert(string value, out AutomobileComment result) { result = new AutomobileComment { Comment = value }; return true; } }
其中有几个要点,
AutomobileCommentTypeConverter
。而后咱们修改Program.cs, 使用CsvParser
来解析sample.txt
CsvParserOptions csvParserOptions = new CsvParserOptions(true, ','); var csvParser = new CsvParser<Automobile>(csvParserOptions, new CsvAutomobileMapping()); var records = csvParser.ReadFromFile("sample.txt", Encoding.UTF8); foreach (var r in records) { if (r.IsValid) { Console.WriteLine(r.Result.ToString()); } }
最终结果
从结果上看,Tiny CSV Parser实现了大部分CSV解析的痛点,惟一不支持的是字符串换行,这一点须要注意。
文章的最后,做者使用Benchmark对CSVHelper和Tiny CSV Parser进行了效率比较。
测试代码以下:
[MemoryDiagnoser] public class CsvBenchmarking { [Benchmark(Baseline =true)] public IEnumerable<Automobile> CSVHelper() { TextReader reader = new StreamReader("import.txt"); var csvReader = new CsvReader(reader); var records = csvReader.GetRecords<Automobile>(); return records.ToList(); } [Benchmark] public IEnumerable<Automobile> TinyCsvParser() { CsvParserOptions csvParserOptions = new CsvParserOptions(true, ','); var csvParser = new CsvParser<Automobile>(csvParserOptions, new CsvAutomobileMapping()); var records = csvParser.ReadFromFile("import.txt", Encoding.UTF8); return records.Select(x => x.Result).ToList(); } }
当测试100000行数据的时候
当测试1000000行数据的时候
从测试结果上看
Tiny Csv Parser的效率比CSVHelper高不少,内存占用也少不少。