开源项目备忘-使用 CsvHelper 简化C#的 CSV 操作
使用 CsvHelper 简化C#的 CSV 操作
前言
CSV 文件以纯文本形式存储表格数据,每行代表一条记录,各字段之间通常以逗号作为分隔符。但在实际应用中,也可能使用分号、制表符等其他字符作为分隔符。它被广泛应用于数据存储和交换。
在软件开发中,使用 CSV 文件是常见任务。CsvHelper 是用于处理 CSV 文件的 .NET 库。本文我们将了解 CsvHelper 的使用方法。
CsvHelper
1、概述
CsvHelper 是 .NET 三方库,它可以运行在 .NET Core 和 .NET Framework 中, 它提供了简单的API 来读写CSV文件,使得处CSV数据更加容易。
// 官网地址
https://joshclose.github.io/CsvHelper/
2、特征
CsvHelper 具有以下几个特征:
-
快:动态编译类以获得极快的性能;
-
灵活:写作时保守,阅读时自由;
-
易于使用:读取和写入与 GetRecords<T>和 WriteRecords一样简单;
-
内存使用率低:读取记录将产生结果,因此一次只有一条记录在内存中;
-
高度可配置:功能丰富的映射和属性系统,可将任何类型的 CSV 文件配置为任何类型的类;
-
开源:完全免费用于商业用途。在 MS-PL 和 Apache 2下获得双重许可;
3、使用要求
在C#中使用 CsvHelper 库读取和解析CSV文件,首先需要安装 CsvHelper 库。我们通过 NuGet 包管理器来安装或通过指令方式安装。
// 包管理控制台
PM> Install-Package CsvHelper
// .NET 命令行控制台
> dotnet add package CsvHelper
// 库文件
CsvHelper.dll
安装完成后,我们就可以引入CsvHelper 命名空间,后续根据提供的API进行读写文件。
4、模块
模块 | 描述 |
---|---|
CsvHelper | 读写 Csv 数据的核心类 |
CsvHelper.Configuration | 配置 CsvHelper 读写行为类 |
CsvHelper.Configuration.Attributes | 配置 CsvHelper 的特性 |
CsvHelper.Expressions | 生成 LINQ 表达式的类 |
CsvHelper.TypeConversion | 将 Csv 的字段与 .NET 类型相互转换的类 |
5、配置信息
CsvHelper 的配置非常灵活,可以通过CsvConfiguration类进行各种设置(如默认分隔符、默认行尾和格式等)。
-
Delimiter:指定字段分隔符,默认为逗号。
-
HasHeaderRecord:指定CSV文件是否有标题记录。
-
IgnoreBlankLines:忽略空行。
-
AllowComments:允许注释行。
-
BadDataFound:处理包含不良数据的行。
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
// 换行
NewLine = Environment.NewLine,
// 指定字段分隔符
Delimiter = ";",
// 定CSV文件是否有标题记录
HasHeaderRecord = false,
// 是否忽略空行
IgnoreBlankLines = true,
// 是否允许注释行
AllowComments = true,
// 处理包含不良数据的行
BadDataFound = null
};
6、使用特性
CsvHelper 提供了多种特性,用于配置字段的映射和处理方式。当我们的类属性名称与CSV文件头名称不匹配时,则可使用特性进行匹配。
-
Index:标记字段顺序,适用于没有标题的CSV文件。
-
Name:指定字段名称,当字段名称与列名不一致时使用。
-
Ignore:忽略某个字段,不将其写入CSV文件。
-
Optional:标记字段为可选,当字段缺失时不会抛出异常。
public class Product
{
[Index(0)]
[Name("ID")]
public int Id { get; set; }
[Index(1)]
public string Name { get; set; }
[Index(2)]
public string Barcode { get; set; }
[Index(3)]
public string Category { get; set; }
[Index(4)]
public string Description { get; set; }
[Ignore]
public double Price { get; set; }
}
读取
1、Csv 文件内容
ID,Name,Barcode,Category,Description
10001,Apple,A202502150001,fruit,红富士苹果
10002,Grape,G202502152003,fruit,阳光葡萄
2、读取所有记录
通过使用 GetRecords 方法将返回一个 IEnumerable<T>,在迭代记录时,一次只返回一条记录。这也意味着只有一小部分文件被读入内存。
static void Main(string[] args)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
// 换行
NewLine = Environment.NewLine,
// 指定字段分隔符
Delimiter = ",",
// 定CSV文件是否有标题记录
HasHeaderRecord = true,
// 是否忽略空行
IgnoreBlankLines = true,
// 是否允许注释行
AllowComments = true,
// 处理包含不良数据的行
BadDataFound = null
};
string csvPath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "product.csv");
using (var reader = new StreamReader(csvPath))
using (var csv = new CsvReader(reader,config))
{
var records = csv.GetRecords<Product>();
foreach (var record in records)
{
//取出每条数据
Console.WriteLine($"ID:{record.Id}, Name:{record.Name}, Barcode:{record.Barcode},Category:{record.Category},Description:{record.Description}");
}
}
Console.Read();
}
3、逐行读取
在处来大型CSV文件时,我们不希望一次性将所有数据加载到内存中,而是使用逐条读取CSV文件中的记录方式读取数据。使用CsvReader.Read 方法来实现。
static void Main(string[] args)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
// 换行
NewLine = Environment.NewLine,
// 指定字段分隔符
Delimiter = ",",
// 定CSV文件是否有标题记录
HasHeaderRecord = true,
// 是否忽略空行
IgnoreBlankLines = true,
// 是否允许注释行
AllowComments = true,
// 处理包含不良数据的行
BadDataFound = null
};
string csvPath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "product.csv");
using (var reader = new StreamReader(csvPath))
using (var csv = new CsvReader(reader, config))
{
while (csv.Read())
{
var record = csv.GetRecord<Product>();
Console.WriteLine($"ID:{record.Id}, Name:{record.Name}, Barcode:{record.Barcode},Category:{record.Category},Description:{record.Description}");
}
}
Console.Read();
}
4、读取单个字段
通过GetField 方法来读取某些字段的值,使用字段的索引或名称读取。static void Main(string[] args)
{
string csvPath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "product.csv");
using (var reader = new StreamReader(csvPath))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var id = csv.GetField<int>(0);
var name = csv.GetField<string>("Name");
Console.WriteLine($"ID:{id},Name:{name}");
}
}
Console.Read();
}
写入
1、写入所有记录
根据类的属性顺序,将数据一次性写入所有记录,通过使用 WriteRecords 方法。static void Main(string[] args)
{
string csvWritePath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "fisiProduct.csv");
var records = new List<Product>
{
new Product { Id = 5001, Name = "Snakehead",Barcode="F2025021550001",Category="Fish",Description="黑鱼"},
new Product { Id = 6001, Name = "Carp",Barcode="F2025021550001",Category="Fish",Description="鲤鱼" },
};
using (var streamWriter = new StreamWriter(csvWritePath))
using (var csv = new CsvWriter(streamWriter, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
Console.Read();
}
2、逐行写入
当需要逐条写入记录,可以参考下代码方式来实现。static void Main(string[] args)
{
string csvWritePath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "fisiProduct.csv");
var records = new List<Product>
{
new Product { Id = 5001, Name = "Snakehead",Barcode="F2025021550001",Category="Fish",Description="黑鱼"},
new Product { Id = 6001, Name = "Carp",Barcode="F2025021550001",Category="Fish",Description="鲤鱼" },
};
using (var streamWriter = new StreamWriter(csvWritePath))
using (var csv = new CsvWriter(streamWriter, CultureInfo.InvariantCulture))
{
foreach (var record in records)
{
csv.WriteRecord(record);
// 换行
csv.NextRecord();
}
}
Console.Read();
}
3、逐个字段写入
使用逐字段写入CSV文件的方式可以更灵活地控制字段的写入顺序,可以参考下代码方式来实现:static void Main(string[] args)
{
string csvWritePath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "fisiProduct.csv");
var records = new List<Product>
{
new Product { Id = 5001, Name = "Snakehead",Barcode="F2025021550001",Category="Fish",Description="黑鱼"},
new Product { Id = 6001, Name = "Carp",Barcode="F2025021550001",Category="Fish",Description="鲤鱼" },
};
using (var streamWriter = new StreamWriter(csvWritePath))
using (var csv = new CsvWriter(streamWriter, CultureInfo.InvariantCulture))
{
csv.WriteHeader<Product>();
csv.NextRecord();
foreach (var record in records)
{
csv.WriteField(record.Id);
csv.WriteField(record.Name);
csv.WriteField(record.Barcode);
csv.NextRecord();
}
}
Console.Read();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!