C# .NET使用CsvHelper读写CSV文件,快速又易用
作者:admin 时间:2023-5-18 20:40:28 浏览:C# .NET 解析并读写 CSV 文件,本文详细介绍如何使用 CsvHelper 来完成这个操作。
CsvHelper 介绍
CsvHelper 是一个用于读取和写入CSV文件的.NET库,非常快速、灵活且易于使用。
CsvHelper 有如下几大特征:
- 快速
即时编译类以获得极快的性能。 - 灵活
写入时保守,读取时自由。 - 便于使用
读取和写入就像GetRecords<T>()
和WriteRecords(records)
一样简单。无需配置。 - 高度可配置
具有丰富的映射和属性系统,可将任何类型的 CSV 文件配置为任何类型的类。 - 优雅的回退
当读取非标准文件时,回退将匹配 MS Excel 解析。 - 可在任何地方运行
CsvHelper 建立在 .NET Standard 2.0 之上,这使得它几乎可以在任何地方运行。如果需要,可以使用旧版本的 .NET。 - 符合RFC 4180
遵守 RFC 4180 标准以确保跨系统的兼容性。 - 内存使用率低
读取记录将产生结果,因此一次只有一条记录在内存中。 - 开源
许多贡献者帮助使 CsvHelper 成为今天伟大的库。完全免费用于商业用途。在MS-PL和 Apache 2下获得双重许可。 - Linux模式
常见 Linux/SerDe 文件的模式,其中使用转义字符而不是 RFC 4180 的字段引用。 - 字段缓存
CSV 文件中存在重复数据时使用字段缓存的选项。这将减少内存并加快解析时间。
CsvHelper 官网:
https://joshclose.github.io/CsvHelper/
开始安装使用
下载
访问下面官方地址下载安装包。
https://www.nuget.org/packages/CsvHelper/
安装
包管理器控制台
PM> Install-Package CsvHelper
.NET CLI 控制台
> dotnet add package CsvHelper
文化资讯
CsvHelper 要求你指定要使用的 CultureInfo
,用于确定类型转换时的默认分隔符、默认行结束符和格式。你也可以更改其中任何一个配置,为你的数据选择合适的区域性。InvariantCulture
在读写文件时可移植性最强,因此将在大多数示例中使用。
换行符
默认情况下,CsvHelper 将遵循RFC 4180并用\r\n
编写换行符,无论你在什么操作系统上运行。CsvHelper 可以读取\r\n
、\r
或\n
不进行任何配置更改。如果要以非标准格式读取或写入,可以更改NewLine
。
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
NewLine = Environment.NewLine,
};
读 CSV 文件
假设我们有如下所示的 CSV 文件。
Id,Name
1,one
2,two
和一个看起来像这样的类定义。
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
如果我们的类属性名称与我们的 CSV 文件头名称相匹配,我们可以在不进行任何配置的情况下读取该文件。
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Foo>();
}
该GetRecords<T>
方法将返回一个IEnumerable<T>
记录,这意味着当你迭代记录时,一次只返回一条记录。这也意味着只有一小部分文件被读入内存。不过要小心。如果你执行任何执行 LINQ
投影的操作,例如调用.ToList()
,整个文件将被读入内存。CsvReader
是只向前的,所以如果你想对你的数据运行任何 LINQ
查询,你必须将整个文件拉入内存。
假设我们的 CSV 文件名与我们的类属性略有不同,我们不想让我们的属性匹配。
id,name
1,one
2,two
在这种情况下,名称是小写的。我们希望我们的属性名称是 Pascal 案例,这样我们就可以更改我们的属性与标题名称的匹配方式。
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
PrepareHeaderForMatch = args => args.Header.ToLower(),
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Foo>();
}
使用配置的 PrepareHeaderForMatch
,我们可以更改标头匹配的方式来匹配属性名称。标头和属性名称都在函数中运行 PrepareHeaderForMatch
。当读者需要找到要为标题设置的属性时,它们现在将匹配。你可以使用此功能做其他事情,例如删除空格或其他字符。
假设 CSV 文件根本没有标题。
1,one
2,two
首先我们需要告诉读者没有头记录,使用配置。
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Foo>();
}
CsvReader
将使用属性在类中的位置作为索引位置。但是这有一个问题,你不能依赖 .NET 中类成员的顺序。我们可以通过将属性映射到 CSV 文件中的某个位置来解决这个问题。
一种方法是使用属性映射。
public class Foo
{
[Index(0)]
public int Id { get; set; }
[Index(1)]
public string Name { get; set; }
}
IndexAttribute
允许你指定要将 CSV 字段用于属性的哪个位置。
你也可以按名称映射,让我们使用之前的小写标题示例,看看我们如何使用属性而不是更改标题匹配。
public class Foo
{
[Name("id")]
public int Id { get; set; }
[Name("name")]
public string Name { get; set; }
}
你还可以使用许多其他属性,请参阅如下网址。
https://joshclose.github.io/CsvHelper/examples/configuration/attributes
如果我们无法控制要映射到的类,所以我们无法向其添加属性怎么办?这种情况下,我们可以使用fluentClassMap
来做映射。
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id).Name("id");
Map(m => m.Name).Name("name");
}
}
要使用映射,我们需要在上下文中注册它。
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<FooMap>();
var records = csv.GetRecords<Foo>();
}
创建类映射是在 CsvHelper 中映射文件的推荐方法,因为它更强大。
你也可以手动读取行。
using (var reader = new StreamReader("path\\to\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = csv.GetRecord<Foo>();
// Do something with the record.
}
}
Read
将向前进行,ReadHeader
会将行读入 CsvHelper 作为标题值。ReadHeader
分隔Read
并允许你在移动到下一行之前对标题行执行其他操作。GetRecord
也不会提前让阅读器允许你对你可能需要做的行做其他事情。你可能需要GetField
针对单个字段或GetRecord
多次调用以填充多个对象。
写 CSV 文件
现在让我们看看如何写入 CSV 文件。它基本上是同一件事,但顺序相反。
让我们使用与以前相同的类定义。
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
我们有一组这样的记录。
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
new Foo { Id = 2, Name = "two" },
};
我们可以将记录写入文件而无需任何配置。
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
该WriteRecords
方法会将所有记录写入文件。写入完成后,你应该调用writer.Flush()
以确保写入器内部缓冲区中的所有数据都已刷新到文件中。一旦一个using
块退出,writer
就会自动刷新,所以我们不必在这里明确地这样做。建议始终用using
块包裹任何IDisposable
对象。该对象将在using
块退出后尽快处理自身(在我们的例子中也是 flush
) 。
还记得我们如何不能依赖 .NET 中的属性顺序吗?如果我们正在编写一个具有标题的类,那没关系,只要我们稍后使用标题进行阅读即可。如果我们想在 CSV 文件中定位标题,我们需要指定一个索引来保证它的顺序。建议在编写时始终设置索引。
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id).Index(0).Name("id");
Map(m => m.Name).Index(1).Name("name");
}
}
你也可以手写行。
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteHeader<Foo>();
csv.NextRecord();
foreach (var record in records)
{
csv.WriteRecord(record);
csv.NextRecord();
}
}
WriteHeader
不会让你前进到下一行。 如果需要,NextRecord
从WriteHeader
中分离允许你在标题中写入更多内容。WriteRecord
也不会使你前进到下一行,从而使你能够写入多个对象或用WriteField
写入单个字段。
示例
CsvHelper 读写CSV文件的示例,请参阅以下文章:
总结
本文详细介绍了 C# .NET 使用 CsvHelper 读写CSV文件的方法。CSVHelper 是在 C# 中使用 CSV 的鼻祖,它根本不需要做任何映射,它自己处理双引号、换行和枚举解析,它可以处理完全自定义的映射、自定义类型转换。此外,它的速度又快又易用,并且还是免费的可用于商用用途,真的是一个让人惊叹的库。
相关文章
- 站长推荐