CsvHelper 入门指南(C# 读写 CSV 文件库)
安装
包管理器控制台
.NET CLI 控制台
先决条件
使用此文档时,默认您具备一些基础的 .NET 知识。请检查先决条件,确保您已理解它们。先决条件
文化信息(CultureInfo)
CsvHelper 要求您指定想要使用的 CultureInfo
。文化信息用于确定默认分隔符、默认行尾字符以及类型转换时的格式化。如果您愿意,也可以更改这些配置中的任何一个。请为您的数据选择合适的文化。InvariantCulture
对于编写文件并在之后再次读取是最便携的,因此大多数示例中都会使用它。
新行
默认情况下,CsvHelper 会遵循 RFC 4180,无论运行在哪个操作系统上,都使用 \r\n
来写入新行。CsvHelper 无需任何配置更改就能读取 \r\n
、 \r
或 \n
。如果您想以非标准格式读取或写入,可以更改 NewLine
的配置。
读取 CSV 文件
假设我们有一个如下所示的 CSV 文件。
以及一个这样的类定义。
如果我们的类属性名称与 CSV 文件的标题名称匹配,我们可以无需任何配置就可读取文件。
GetRecords
方法将返回一个 IEnumerable
,它会按需 yield
记录。这意味着当你遍历记录时,每次只返回一条记录。这也意味着只有文件的一小部分被读入内存。但请注意,如果您执行任何 LINQ 投影操作,如调用 .ToList()
,则整个文件将被读入内存。CsvReader
是单向的,所以如果你想对数据运行任何 LINQ 查询,你需要将整个文件加载到内存中。只需知道你在做的是什么即可。
假设我们的 CSV 文件名与类属性略有不同,且我们不想让属性匹配。
在这种情况下,名称都是小写的。我们希望属性名称为帕斯卡命名法(Pascal Case),所以我们只需改变属性名与标题名的匹配方式。
使用配置 PrepareHeaderForMatch
,我们能够改变头匹配属性名称的方式。标题和属性名称都会通过 PrepareHeaderForMatch
函数处理。当读取器需要找到与标题对应的属性时,它们现在就能匹配了。你可以使用这个函数来做其他事情,比如移除空白字符或其他字符。
假设我们的 CSV 文件根本没有标题。
首先,我们需要通过配置告诉读取器没有标题记录。
CsvReader
将使用类中属性的位置作为索引位置。但这存在一个问题:不能依赖于.NET 中的成员排序。我们可以通过映射属性到 CSV 文件中的位置来解决这个问题。
一种方法是使用属性映射。
IndexAttribute
允许您指定 CSV 字段的位置,该位置用于属性。
你也可以按名称映射。让我们使用之前的全小写标题示例,看看如何使用属性而非改变标题匹配方式。
如果我们无法控制要映射的类,从而无法向其添加属性该怎么办?在这种情况下,我们可以使用流畅的 ClassMap
来进行映射。
为了使用映射,我们需要在上下文中注册它。
创建类映射是 CsvHelper 推荐的文件映射方式,因为它功能更强大。
你也可以手动读取行。
Read
会前进到下一行。ReadHeader
会读取行作为标题值。将 Read
和 ReadHeader
分开,允许在移动到下一行之前对标题行进行其他操作。GetRecord
也不会前进读取器,允许你对可能需要做的行进行其他操作。你可能需要 GetField
获取单个字段,或者多次调用 GetRecord
填充多个对象。
写入 CSV 文件
现在我们来看看如何写入 CSV 文件。基本上是相同的过程,只是顺序相反。
我们仍然使用之前的类定义。
我们有一组这样的记录。
我们可以无需任何配置就将记录写入文件。
WriteRecords
方法会将所有记录写入文件。写入完成后,建议调用 writer.Flush()
以确保写入器内部缓冲区的所有数据都已刷新到文件中。一旦 using
块退出,写入器会自动刷新,所以在这种情况下我们不需要显式去做。推荐总是使用 using
块包裹任何 IDisposable
对象。对象会在 using
块退出后尽快自行释放(并在此例中也会刷新)。
还记得我们不能依赖.NET 中的属性顺序吗?如果我们正在写入一个带有标题的类,没关系,只要我们稍后使用标题进行读取。如果我们想在 CSV 文件中定位标题的顺序,我们需要指定索引来保证其顺序。写入时推荐始终设置索引。
你也可以手动写入行。
WriteHeader
不会使你前进到下一行。将 NextRecord
与 WriteHeader
分开,允许在需要时在标题行中写入更多信息。WriteRecord
也不会使你前进到下一行,以便你有能力写入多个对象或使用 WriteField
写入单个字段。