.NET Core中的CSV解析庫
感謝
本篇首先特別感謝從此啟程兄的《.NetCore外國一些高質量博客分享》, 發現很多國外的.NET Core技術博客資源, 我會不定期從中選擇一些有意思的文章翻譯總結一下。
.NET Core中的CSV解析庫
本篇博客來源於.NET Core Totorials的《CSV Parsing In .NET Core》。
背景介紹
對於初級程序員來說, 使用string.Split(‘,‘)來解析CSV文件基本就是唯一可行的方法, 但是之後他們會發現除了使用逗號分隔值之外,CSV中還有其他需要處理的東西,所以作者就介紹了CSV解析的一些痛點並推薦了2個比較好用CSV解析庫。
CSV解析一些痛點
- 一個CSV文件有可能有表頭,也可能沒有表頭。如果表頭存在的話,解析CSV時,列的順序就不太重要了,因為你可以根據表頭知道所需的數據在第幾列。如果表頭不存在的話,解析CSV時,就需要依賴列的順序。所以CSV的解析,應該即支持表頭,也支持按列的順序。
- CSV文件中某一列的值可能是帶雙引號的字符串,字符串中可能包含換行符、逗號,雙引號。
- 例1:1,2,"a,b"
- 例2: 1,2,"a[換行符]b"
- 例3: 1,2,"this is ""Good""." (註:雙引號字符串中的出現的連續雙引號表示轉義,這裏真正的文本是this is "Good".)
- CSV文件中每一行的數據的數據列數量“應該”一樣,但不是必須一樣,所以解析CSV需要處理這些不一致的情況
- 在.NET中,當反序列化一個CSV文件的時候,還需要
- 支持反序列化成集合
- 支持枚舉
- 支持自定義映射
- 支持映射嵌套對象
.NET Core中的一些優秀CSV解析庫
這裏作者推薦了2個CSV解析庫,一個是CSVHelper, 一個是Tiny CSV Parser。
測試例子
為了測試這些CSV解析庫,我們首先創建一個.NET Core的控制臺程序
然後我們添加一個Automobile類,其代碼如下
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
類的對象實例中。
其內容如下
Make,Model,Type,Year,Price,Comment
"Toyota",Corolla,Car,1990,2000.99,"Comment with a,
line break and "" quotes"
這個文件中第一行是一個表頭,第二行是一個數據行,數據行中包含了
- 字符串內容換行
- 字符串中有逗號
- 字符串中有雙引號
CSVHelper
CSVHelper是一個CSV文件的讀寫庫。它支持讀寫自定義類對象。官網地址https://joshclose.github.io/CsvHelper/
安裝
我們可以使用Package Manager Console來安裝CSVHelper。
命令如下:
PM> Install-Package CsvHelper
解析CSV
使用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都處理的很成功。
Tiny CSV Parser
下一個介紹的CSV解析器是Ting CSV Parser, 官網http://bytefish.github.io/TinyCsvParser/index.html, 它是使用配置的方式映射CSV字段, 使用方式上有點類似於AutoMapper
安裝
我們可以使用Package Manager Console來安裝Tiny CSV Parser。
命令如下:
PM> Install-Package TinyCsvParser
解析CSV
使用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;
}
}
其中有幾個要點,
- MapProperty是根據列的索引來映射屬性的。
- 當映射枚舉時,需要使用EnumConverter來映射。
- 當映射子對象的時候,需要創建子對象對應的Converter, 例如
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高很多,內存占用也少很多。
最終結論
- 當不需要支持字符串換行的時候,請使用Tiny Csv Parser
- 當需要支持字符串換行的時候,請使用CSVHelper
附源代碼
作者:Lamond Lu
出處:https://www.cnblogs.com/lwqlun/p/9639456.html
本站使用「署名 4.0 國際」創作共享協議,轉載請在文章明顯位置註明作者及出處。
.NET Core中的CSV解析庫