Cinchoo ETL - CSV Lite Reader
.NET 的简单快速轻量级 CSV 文件读取器
1. 引言
ChoETL
是一个开源的 .NET ETL (提取、转换和加载) 框架。它是一个基于代码的库,用于从多个源提取数据、转换数据,并将数据加载到 .NET 环境中的数据仓库中。您可以快速地将数据加载到数据仓库中。本文将介绍 ChoETL
框架提供的 CSVReader
组件。这是一个简单的实用类,用于从文件/源提取 CSV 数据。
特点
- 超快速 CSV 读取器,快速解析 CSV 文件 (100 万行,20 列,大约需要 7 秒)
- 基于流的解析器可提供极致的性能、低资源使用率以及几乎无限的通用性,可扩展到任何大小的数据文件,甚至数十或数百 GB
- 遵循 CSV 标准文件规则 (多行、带引号的列等)
- 除了逗号,还可以使用大多数分隔字符,包括制表符分隔的字段。
- 公开对象的
IEnumarable
列表 - 通常与 LINQ 查询一起用于投影、聚合和过滤等操作 - 支持延迟读取
- 能够从 CSV 文件获取类型化的对象列表
2. 要求
此框架库使用 C# 编写,基于 .NET 4.5 Framework / .NET Core 2.x。
3. “Hello World!” 示例
- 打开 VS.NET 2013 或更高版本
- 创建一个示例 VS.NET (.NET Framework 4.5) 控制台应用程序项目
- 根据 .NET 环境,使用 Nuget 命令通过 程序包管理器控制台 安装 ChoETL
Install-Package ChoETL
Install-Package ChoETL.NETStandard
- 使用
ChoETL
命名空间 - 使用
ChoCSVLiteReader
类解析 CSV 文件
让我们从查看一个读取包含两列的 CSV 文件的简单示例开始。
列表 3.1 示例 CSV 数据文件 (Emp.csv)
Id,Name
1,Tom
2,Carl
3,Mark
有许多方法可以以最少的设置开始 CSV 文件解析。
3.1. 快速加载 - 数据优先方法
这是 零配置、快速加载 CSV 文件的方式。不需要 POCO 对象。下面的示例代码展示了如何加载文件。
列表 3.1.1 使用迭代器加载 CSV 文件 (最快)
using (var r = new ChoCSVLiteReader())
{
//Open the reader, skip the header
foreach (var cols in r.ReadFile("emp.csv").Skip(1))
{
Console.WriteLine($"Id: {cols[0]}");
Console.WriteLine($"Name: {cols[1]}");
}
}
示例 Fiddle: https://dotnetfiddle.net/kWhr27
列表 3.1.2 使用循环加载 CSV 文件 (最快)
using (var r = new ChoCSVLiteReader())
{
//Open the reader, skip the header
var recNum = r.ReadFile("emp.csv").Skip(1).GetEnumerator();
while (recNum.MoveNext())
{
var cols = recNum.Current;
Console.WriteLine($"Id: {cols[0]}");
Console.WriteLine($"Name: {cols[1]}");
}
}
示例 Fiddle: https://dotnetfiddle.net/bV7nq5
您也可以按名称访问 csv
字段。下面的示例展示了如何按名称访问它们。
使用 ChoDynamicObject (一种特殊的 ExpandoObject)
列表 3.1.3 使用列名加载 CSV 文件 (使用 ChoDynamicObject)
using (var r = new ChoCSVLiteReader())
{
foreach (dynamic rec in r.ReadFile<ChoDynamicObject>("emp.csv", true))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
}
示例 Fiddle: https://dotnetfiddle.net/PTnx2L
使用 ExpandoObject
列表 3.1.4 使用列名加载 CSV 文件 (使用 ExpandoObject)
using (var r = new ChoCSVLiteReader())
{
foreach (var rec in r.ReadFile<ExpandoObject>("emp.csv", true))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
}
如果 CSV 文件不带标题,CSVReader
会自动将列命名为 Column1
、Column2
... 在动态对象中。
3.2. 代码优先方法
这是另一种 零配置 的方式,使用 POCO 类解析和加载 CSV 文件。首先,定义一个简单的类来匹配底层 CSV 文件布局。
清单 3.2.1 简单的 POCO 实体类
public partial class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
在上面的示例中,该类定义了两个属性,与示例 CSV 文件模板匹配。
3.2.1 使用用户定义的映射器
下面的示例展示了如何使用自定义用户定义的映射器加载 CSV。
列表 3.2.1.1 使用自定义用户定义的映射器加载 CSV 文件
foreach (var rec in r.ReadFile<EmployeeRec>("emp.csv", true,
mapper: (lineno, cols, rec) =>
{
rec.Id = cols[0].CastTo<int>();
rec.Name = cols[1];
}))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
在上面的示例中,我们通过映射器函数控制将 CSV 值加载到对象成员中。
示例 Fiddle: https://dotnetfiddle.net/NZZ5EK
3.2.2 使用默认内置映射器
下面的示例展示了如何使用 CSV 读取器附带的默认内置映射器加载 CSV 文件。
列表 3.2.2.1 使用内置映射器加载 CSV 文件 (默认映射)
foreach (var rec in r.ReadFile<EmployeeRec>("emp.csv", true))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
在上面的示例中,我们让解析器使用内置映射器功能将 CSV 值加载到对象成员中。默认情况下,内置映射器通过索引将 CSV 列映射到对象成员 (第一列映射到第一个对象成员,第二列映射到第二个对象成员,依此类推)。
示例 Fiddle: https://dotnetfiddle.net/IZRKWT
3.2.3 使用位置内置映射器
如果 CSV 文件中的列顺序与定义的 POCO 模型对象不同,但想按位置映射加载它们,可以通过用 ColumnAttribute
装饰对象成员来指定 CSV 列的映射顺序。
列表 3.2.3.1 带有 OrderAttribute 的 POCO 实体类
public partial class EmployeeRec
{
[Column(Order=1)]
public string Name { get; set; }
[Column(Order=0)]
public int Id { get; set; }
}
列表 3.2.3.2 使用内置映射器加载 CSV 文件 (位置映射)
foreach (var rec in r.ReadFile<EmployeeRec>("emp.csv", true))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
在上面的示例中,解析器在解析过程中使用 OrderAttribute 将 CSV 列映射到相应的对象成员。
示例 Fiddle: https://dotnetfiddle.net/fwd3j5
3.2.4 使用命名内置映射器
如果 CSV 文件中的列标题与定义的 POCO 模型对象成员不匹配,您可以使用 DisplayNameAttribute / DisplayAttribute
来指定 CSV 列名到对象成员的匹配。
列表 3.2.4 带有 DisplayNameAttribute 的 POCO 实体类
public partial class EmployeeRec
{
[DisplayName("Id")]
public int Identifier { get; set; }
[DisplayName("Name")]
public string GivenName { get; set; }
}
列表 3.2.5 使用内置映射器加载 CSV 文件 (名称映射)
foreach (var rec in r.ReadFile<EmployeeRec>("emp.csv", true))
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
在上面的示例中,解析器在解析过程中使用显示属性将 CSV 列映射到相应的对象成员。
示例 Fiddle: https://dotnetfiddle.net/K65Ywq
3.2. 其他读取器方法
非泛型重载
ReadText
- 解析 csv 文本,返回string[]
ReadFile
- 解析 csv 文件,返回string[]
Read
- 解析 csv 流,返回string[]
ReadLines
- 解析 csv 行,返回string[]
泛型重载
ReadText<T>
- 解析 csv 文本,返回T
ReadFile<T>
- 解析 csv 文件,返回T
Read<T>
- 解析 csv 流,返回T
ReadLines<T>
- 解析 csv 行,返回T
3.3. CSV 到 DataTable
Cinchoo ETL 库提供了一个辅助方法,可以将任何可枚举对象转换为 DataTable
。此转换有助于将 CSV 数据绑定到网格/其他控件/在内存中处理等。
下面的示例展示了如何将 CSV 转换为 DataTable
。
列表 3.3.1 将 CSV 转换为 DataTable
using (var r = new ChoCSVLiteReader())
{
var recs = r.ReadFile<EmployeeRec>("emp.csv", true);
var dt = recs.AsDataTable();
}
3.4. CSV 到 DataReader
Cinchoo ETL 库提供了一个辅助方法,可以将任何可枚举对象转换为 DataReader
。此转换有助于通过 ADO.NET/EF 处理 (导出) 大量 CSV 文件到任何数据库。
下面的示例展示了如何将 CSV 转换为 DataReader
。
列表 3.4.1 将 CSV 转换为 DataReader
using (var r = new ChoCSVLiteReader())
{
var recs = r.ReadFile<EmployeeRec>("emp.csv", true);
var dr = recs.AsDataReader();
}
有关 Cinchoo ETL 的更多信息,请访问其他 CodeProject 文章。
历史
- 2021 年 12 月 30 日:初始版本