|
|
|
|
|
|
關于使用C#解析、讀寫CSV數據,前面我已經介紹過幾個方法,可參閱下面文章。
本文中,我將介紹另一個方法,那就是使用 TinyCsvParser , 一個CSV解析器。
TinyCsvParser 介紹
TinyCsvParser 是一個 .NET 庫,用于以簡單的方式解析 CSV 數據,同時提供非常高性能和非常干凈的 API。它是高度可配置的,以提供最大的靈活性。
TinyCsvParser 支持 .NET Core。
我們可以在 github 查看 TinyCsvParser 的最新信息、使用說明及發行版本,并下載它:
https://github.com/bytefish/TinyCsvParser
我們還可以使用 NuGet 包。要安裝 TinyCsvParser ,請在程序包管理器控制臺中運行以下命令。
PM> Install-Package TinyCsvParser
基本用法
想象一下,我們在 CSV 文件中有一個人的列表,其中包含他們的名字、姓氏和生日。
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02
我們系統中相應的域模型可能如下所示。
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
使用TinyCsvParser時,必須定義 CSV 數據中的列與域模型中的屬性之間的映射。
public class CsvPersonMapping : CsvMapping<Person>
{
public CsvPersonMapping()
: base()
{
MapProperty(0, x => x.FirstName);
MapProperty(1, x => x.LastName);
MapProperty(2, x => x.BirthDate);
}
}
然后我們可以通過CsvParser使用映射來解析。
namespace TinyCsvParser.Test
{
[TestFixture]
public class TinyCsvParserTest
{
[Test]
public void TinyCsvTest()
{
CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
CsvReaderOptions csvReaderOptions = new CsvReaderOptions(new[] { Environment.NewLine });
CsvPersonMapping csvMapper = new CsvPersonMapping();
CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);
var stringBuilder = new StringBuilder()
.AppendLine("FirstName;LastName;BirthDate")
.AppendLine("Philipp;Wagner;1986/05/12")
.AppendLine("Max;Mustermann;2014/01/01");
var result = csvParser
.ReadFromString(csvReaderOptions, stringBuilder.ToString())
.ToList();
Assert.AreEqual(2, result.Count);
Assert.IsTrue(result.All(x => x.IsValid));
// Asserts ...
}
}
}
就是這樣!示例中的CsvParserOptions設置為跳過標題,使用\n為行分隔符和;為列分隔符。
高級用法
現在想象一下,你的客戶突然將一個人的生日更改為一種奇怪的格式,并像這樣寫日期2004###01###25。我們無法使用默認轉換器解析此類日期格式,但在TinyCsvParser中,我們可以輕松定義DateTimeConverter具有自定義日期時間格式。
映射屬性時,你可以使用WithCustomConverter方法覆蓋默認轉換器。我們用給定的格式yyyy###MM###dd實例化DateTimeConverter。
private class CsvPersonMappingWithCustomConverter : CsvMapping<Person>
{
public CsvPersonMappingWithCustomConverter()
{
MapProperty(0, x => x.FirstName);
MapProperty(1, x => x.LastName);
MapProperty(2, x => x.BirthDate)
.WithCustomConverter(new DateTimeConverter("yyyy###MM###dd"));
}
}
然后讓我們編寫一個單元測試來驗證預期的結果。
[Test]
public void WeirdDateTimeTest_CustomConverterBased()
{
CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
CsvReaderOptions csvReaderOptions = new CsvReaderOptions(new[] { Environment.NewLine });
CsvPersonMappingWithCustomConverter csvMapper = new CsvPersonMappingWithCustomConverter();
CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);
var stringBuilder = new StringBuilder()
.AppendLine("FirstName;LastName;BirthDate")
.AppendLine("Philipp;Wagner;1986###05###12");
var result = csvParser
.ReadFromString(csvReaderOptions, stringBuilder.ToString())
.ToList();
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);
}
就是這樣!
解析器返回一個ParallelQuery,可用于對數據執行其他處理。我認為 LINQ 是 C# 中最神奇的東西之一!我能夠在根本不處理鎖或線程的情況下并行化整個解析。
在這個例子中,我們將解析數據并搜索所有 first name 含有Philipp的名字 。看看我們如何根本不需要編寫 if 語句?
[Test]
public void ParallelLinqTest()
{
CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
CsvReaderOptions csvReaderOptions = new CsvReaderOptions(new[] { Environment.NewLine });
CsvPersonMapping csvMapper = new CsvPersonMapping();
CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);
var stringBuilder = new StringBuilder()
.AppendLine("FirstName;LastName;BirthDate")
.AppendLine("Philipp;Wagner;1986/05/12")
.AppendLine("Max;Mustermann;2014/01/01");
var result = csvParser
.ReadFromString(csvReaderOptions, stringBuilder.ToString())
.Where(x => x.IsValid)
.Where(x => x.Result.FirstName == "Philipp")
.ToList();
Assert.AreEqual(1, result.Count);
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);
}
總結
本文介紹了一個解析CSV數據的方法——TinyCsvParser,它在 C# 中構建了一個干凈、易于使用且高性能的 CSV 解析庫。
有關解析CSV的方法,我曾介紹過其他一些,你可參閱以下文章:
