|
|
|
|
|
關于使用C#解析、讀寫CSV數(shù)據(jù),前面我已經(jīng)介紹過幾個方法,可參閱下面文章。
本文中,我將介紹另一個方法,那就是使用 TinyCsvParser , 一個CSV解析器。
TinyCsvParser 介紹
TinyCsvParser 是一個 .NET 庫,用于以簡單的方式解析 CSV 數(shù)據(jù),同時提供非常高性能和非常干凈的 API。它是高度可配置的,以提供最大的靈活性。
TinyCsvParser 支持 .NET Core。
我們可以在 github 查看 TinyCsvParser 的最新信息、使用說明及發(fā)行版本,并下載它:
https://github.com/bytefish/TinyCsvParser
我們還可以使用 NuGet 包。要安裝 TinyCsvParser ,請在程序包管理器控制臺中運行以下命令。
PM> Install-Package TinyCsvParser
基本用法
想象一下,我們在 CSV 文件中有一個人的列表,其中包含他們的名字、姓氏和生日。
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02
我們系統(tǒng)中相應的域模型可能如下所示。
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
使用TinyCsvParser時,必須定義 CSV 數(shù)據(jù)中的列與域模型中的屬性之間的映射。
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
為行分隔符和;
為列分隔符。
高級用法
現(xiàn)在想象一下,你的客戶突然將一個人的生日更改為一種奇怪的格式,并像這樣寫日期2004###01###25。我們無法使用默認轉(zhuǎn)換器解析此類日期格式,但在TinyCsvParser中,我們可以輕松定義DateTimeConverter
具有自定義日期時間格式。
映射屬性時,你可以使用WithCustomConverter
方法覆蓋默認轉(zhuǎn)換器。我們用給定的格式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"));
}
}
然后讓我們編寫一個單元測試來驗證預期的結(jié)果。
[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,可用于對數(shù)據(jù)執(zhí)行其他處理。我認為 LINQ 是 C# 中最神奇的東西之一!我能夠在根本不處理鎖或線程的情況下并行化整個解析。
在這個例子中,我們將解析數(shù)據(jù)并搜索所有 first name 含有Philipp
的名字 ??纯次覀?nèi)绾胃静恍枰帉?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);
}
總結(jié)
本文介紹了一個解析CSV數(shù)據(jù)的方法——TinyCsvParser,它在 C# 中構(gòu)建了一個干凈、易于使用且高性能的 CSV 解析庫。
有關解析CSV的方法,我曾介紹過其他一些,你可參閱以下文章: