开源项目备忘-使用 CsvHelper 简化C#的 CSV 操作

使用 CsvHelper 简化C#的 CSV 操作

原创 fountyuan dotNet开发技术分享
 来源:https://mp.weixin.qq.com/s/KH5Sjs9o-2PorMiZVEGLTA
 

前言

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 { getset; }
    [Index(1)]
    public string Name { getset; }
    [Index(2)]
    public string Barcode { getset; }
    [Index(3)]
    public string Category { getset; }
    [Index(4)]
    public string Description { getset; }
    [Ignore]
    public double Price { getset; }
}

读取

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 = 5001Name = "Snakehead",Barcode="F2025021550001",Category="Fish",Description="黑鱼"},
        new Product { Id = 6001Name = "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 = 5001Name = "Snakehead",Barcode="F2025021550001",Category="Fish",Description="黑鱼"},
        new Product { Id = 6001Name = "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();
}

小结

CsvHelper 作为一个功能强大、极其快速、灵活且易于使用的.NET库,极大地简化了CSV文件的读写操作。它除提供基本的读写功能外,还提供了丰富的配置选项和高级特性,能够满足各种复杂的数据处理需求。在数据导入导出、数据交换或其他需要处理CSV文件的场景中,CsvHelper 都是可供选择的库。希望本文对您有所收获,如有不到之处,请多多包涵
posted @   firespeed  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起