ZetCode

C# CSV - 读取和写入 CSV 数据

最后修改于 2023 年 7 月 5 日

C# CSV 教程展示了如何在 C# 中读取和写入 CSV 数据。

CSV

CSV (逗号分隔值) 是一种非常流行的导入和导出数据格式,用于电子表格和数据库。CSV 文件中的每一行都是一个数据记录。每个记录由一个或多个字段组成,字段之间用逗号分隔。虽然 CSV 是一种非常简单的数据格式,但可能存在许多差异,例如不同的分隔符、换行符或引用字符。

在本文中,我们使用 CsvHelper 库读取和写入 CSV 数据。

$ dotnet add package CsvHelper

我们需要将 CsvHelper 包添加到我们的项目中。

C# CSV 按记录读取数据

在下面的示例中,我们按记录读取 CSV 文件。

users.csv
FirstName,LastName,Occupation
John,Doe,gardener
Roger,Roe,driver
Lucy,Smith,teacher

我们有这个 users.csv 文件。

Program.cs
using System.Globalization;
using CsvHelper;
using CsvHelper.Configuration;

var csvConfig = new CsvConfiguration(CultureInfo.CurrentCulture)
{
    HasHeaderRecord = false
};

using var streamReader = File.OpenText("users.csv");
using var csvReader = new CsvReader(streamReader, csvConfig);

string value;

while (csvReader.Read())
{
    for (int i = 0; csvReader.TryGetField<string>(i, out value); i++)
    {
        Console.Write($"{value} ");
    }

    Console.WriteLine();
}

Read 方法将读取器推进到下一条记录。我们使用 TryGetField 读取记录的字段。

$ dotnet run
FirstName LastName Occupation 
John Doe gardener 
Roger Roe driver 
Lucy Smith teacher 

C# CSV 将数据读取到对象中

在下一个示例中,我们使用 GetRecords 将数据读取到对象中。

Program.cs
using System.Globalization;
using CsvHelper;

using var streamReader = File.OpenText("users.csv");
using var csvReader = new CsvReader(streamReader, CultureInfo.CurrentCulture);

var users = csvReader.GetRecords<User>();

foreach (var user in users)
{
    Console.WriteLine(user);
}

record User(string FirstName, String LastName, string Occupation);

在该示例中,我们定义了 User 类,并将 users.csv 文件的记录读取到此类的实例中。 GetRecords 返回给定类型的 IEnumerable

$ dotnet run
User { FirstName = John, LastName = Doe, Occupation = gardener }
User { FirstName = Roger, LastName = Roe, Occupation = driver }
User { FirstName = Lucy, LastName = Smith, Occupation = teacher }

C# CSV 配置

在下面的示例中,我们将使用带有分号分隔符和注释的 CSV 文件。 为了解析这种不同的“CSV”文件,我们需要配置解析器。

users.csv
# this is users.csv file

John;Doe;gardener
Roger;Roe;driver
Lucy;Smith;teacher

我们有这个 users.csv 文件。

Program.cs
using System.Globalization;
using CsvHelper;
using CsvHelper.Configuration; 

var csvConfig = new CsvConfiguration(CultureInfo.CurrentCulture)
{
    HasHeaderRecord = false,
    Comment = '#',
    AllowComments = true,
    Delimiter = ";",
};

using var streamReader = File.OpenText("users.csv");
using var csvReader = new CsvReader(streamReader, csvConfig);

while (csvReader.Read())
{
    var firstName = csvReader.GetField(0);
    var lastName = csvReader.GetField(1);
    var occupation = csvReader.GetField(2);

    Console.WriteLine($"{firstName} {lastName} is {occupation}");
}

使用 CsvConfiguration 配置读取器。

var csvConfig = new CsvConfiguration(CultureInfo.CurrentCulture)
{
    HasHeaderRecord = false,
    Comment = '#',
    AllowComments = true,
    Delimiter = ";",
};

我们告诉读取器没有标头,并且注释字符为 #。 我们允许文件中的注释并设置注释的字符。(实际上,我们不必设置注释字符,因为默认情况下使用 #。)我们将分隔符设置为分号字符。(默认情况下忽略空行。)

using var csvReader = new CsvReader(streamReader, csvConfig);

配置文件传递给 CsvReader

$ dotnet run
John Doe is gardener
Roger Roe is driver
Lucy Smith is teacher

C# CSV 引用字段

在下面的示例中,我们展示了如何引用字段。

Program.cs
using System.Globalization;
using CsvHelper;
using CsvHelper.Configuration;

var csvConfig = new CsvConfiguration(CultureInfo.CurrentCulture)
{
    ShouldQuote = args => args.Row.Index == 1
};

var users = new List<User> 
{
    new (1, "John Doe", "gardener", "12/5/1997"),
    new (2, "Lucy Smith", "teacher", "5/12/1983"),
    new (3, "Roger Roe", "driver", "4/2/2001"),
    new (4, "Robert Smith", "cook", "21/11/1976"),
    new (5, "Maria Smith", "accountant", "5/9/1986"),
};

using var fs = new StreamWriter("users.csv");
using var csvWriter = new CsvWriter(fs, csvConfig);

csvWriter.WriteHeader<User>();
csvWriter.NextRecord(); 
csvWriter.WriteRecords(users);

record User(int Id, string Name, string Occupation, string DateOfBirth);

我们有一个用户列表。 我们决定引用每行中的第二个字段。

var csvConfig = new CsvConfiguration(CultureInfo.CurrentCulture)
{
    ShouldQuote = args => args.Row.Index == 1
};

CsvConfiguration 中,我们将 ShouldQuote 属性设置为对第二个字段返回 true。

$ dotnet run
$ cat users.csv 
Id,"Name",Occupation,DateOfBirth
1,"John Doe",gardener,12/5/1997
2,"Lucy Smith",teacher,5/12/1983
3,"Roger Roe",driver,4/2/2001
4,"Robert Smith",cook,21/11/1976
5,"Maria Smith",accountant,5/9/1986

C# CSV WriteField

记录中的字段使用 WriteField 写入。

Program.cs
using System.Globalization;
using System.Text;
using CsvHelper;

var users = new List<User>
{
    new ("John", "Doe", "gardener"),
    new ("Roger", "Roe", "driver"),
    new ("Lucy", "Smith", "teacher"),
};

using var mem = new MemoryStream();
using var writer = new StreamWriter(mem);
using var csvWriter = new CsvWriter(writer, CultureInfo.CurrentCulture);

csvWriter.WriteField("FirstName");
csvWriter.WriteField("LastName");
csvWriter.WriteField("Occupation");
csvWriter.NextRecord();

foreach (var user in users)
{
    csvWriter.WriteField(user.FirstName);
    csvWriter.WriteField(user.LastName);
    csvWriter.WriteField(user.Occupation);
    csvWriter.NextRecord();
}

writer.Flush();
var res = Encoding.UTF8.GetString(mem.ToArray());
Console.WriteLine(res);

record User(string FirstName, string LastName, string Occupation);

在该示例中,我们将 CSV 数据写入内存,然后写入控制台。

csvWriter.WriteField("FirstName");
csvWriter.WriteField("LastName");
csvWriter.WriteField("Occupation");
csvWriter.NextRecord();

首先,我们写入标头。 NextRecord 方法添加一个换行符。

foreach (var user in users)
{
    csvWriter.WriteField(user.FirstName);
    csvWriter.WriteField(user.LastName);
    csvWriter.WriteField(user.Occupation);
    csvWriter.NextRecord();
}

WriteField 将字段写入 CSV 文件。 使用 NextRecord 启动新记录。

writer.Flush();

要实际写入数据,我们需要调用 Flush

var result = Encoding.UTF8.GetString(mem.ToArray());
Console.WriteLine(result);

我们将数据从内存写入控制台。

$ dotnet run
FirstName,LastName,Occupation
John,Doe,gardener
Roger,Roe,driver
Lucy,Smith,teacher

C# CSV 使用 WriteRecords 写入数据

在下面的示例中,我们使用 WriteRecords 一次性写入所有记录。

Program.cs
using System.Globalization;
using CsvHelper;

var users = new List<User> 
{
    new ("John", "Doe", "gardener"),
    new ("Lucy", "Smith", "teacher"),
    new ("Roger", "Roe", "writer"),
};

using var writer = new StreamWriter(Console.OpenStandardOutput());
using var csvWriter = new CsvWriter(writer, CultureInfo.CurrentCulture);

csvWriter.WriteHeader<User>();
csvWriter.NextRecord(); // adds new line after header
csvWriter.WriteRecords(users);

record User(string FirstName, string LastName, string Occupation);

在该示例中,我们将用户对象列表中的数据写入控制台。 WriteHeader 从给定的成员写入标头记录。

C# CSV 自定义解决方案

通常,建议使用现有的库来处理 CSV。 尽管 CSV 看起来很简单,但提供一个强大的解决方案并不容易。(例如,字段可能被引用。)

data.csv
John Doe, gardener, 12/5/1997
Jane Doe, teacher, 5/10/1983
Robert Smith, driver, 4/2/2001
Maria Smith, cook, 9/11/1976

这是 data.csv 文件。

Program.cs
using System.Text;

var path = "data.csv";
var lines = File.ReadLines(path, Encoding.UTF8);

var users = from line in lines
            let fields = line.Replace(", ", ",").Split(",")
            select new User(fields[0], fields[1], DateTime.Parse(fields[2]));

var sorted = from user in users
             orderby user.DateOfBirth descending
             select user;

foreach (var user in sorted)
{
    Console.WriteLine(user);
}

public record User(string Name, string Occupation, DateTime DateOfBirth);

该示例使用 Linq 解析 CSV 文件。 它还按降序排列用户的生日。

$ dotnet run
User { Name = Robert Smith, Occupation = driver, DateOfBirth = 4/2/2001 12:00:00 AM }
User { Name = John Doe, Occupation = gardener, DateOfBirth = 12/5/1997 12:00:00 AM }
User { Name = Jane Doe, Occupation = teacher, DateOfBirth = 5/10/1983 12:00:00 AM }
User { Name = Maria Smith, Occupation = cook, DateOfBirth = 9/11/1976 12:00:00 AM }

C# 将 HTML 表格导出到 CSV 文件

在下一个示例中,我们从网站抓取 HTML 表格并将数据导出到 CSV 文件。

对于网络抓取,我们使用 AngleSharp 库。

Program.cs
using System.Globalization;
using AngleSharp;
using CsvHelper;

var config = Configuration.Default.WithDefaultLoader();
using var context = BrowsingContext.New(config);

var url = "https://nrf.com/resources/top-retailers/top-100-retailers/top-100-retailers-2020";
using var doc = await context.OpenAsync(url);

var htable = doc.GetElementById("stores-list--section-23906");
var trs = htable.QuerySelectorAll("tr").Skip(1);

using var fs = new StreamWriter("data.csv");
using var writer = new CsvWriter(fs, CultureInfo.CurrentCulture);

var rows = new List<Row>();

foreach (var tr in trs)
{
    var tds = tr.QuerySelectorAll("td").Take(3);
    var fields = (from e in tds select e.TextContent).ToArray();
    var row = new Row(fields[0], fields[1], fields[2]);

    rows.Add(row);
}

writer.WriteRecords(rows);

record Row(string Rank, string Company, string Sales);

我们从一个包含 2020 年美国前 100 名零售商的表格中抓取数据。

var config = Configuration.Default.WithDefaultLoader();
using var context = BrowsingContext.New(config);

var url = "https://nrf.com/resources/top-retailers/top-100-retailers/top-100-retailers-2020";
using var doc = await context.OpenAsync(url);

我们设置 AngleSharp 上下文并从提供的链接检索文档。

var htable = doc.GetElementById("stores-list--section-23906");
var trs = htable.QuerySelectorAll("tr").Skip(1);

我们找到 HTML 表格并选择除标头之外的所有行。

using var fs = new StreamWriter("data.csv");
using var writer = new CsvWriter(fs, CultureInfo.CurrentCulture);

我们设置 CsvWriter

foreach (var tr in trs)
{
    var tds = tr.QuerySelectorAll("td").Take(3);
    var fields = (from e in tds select e.TextContent).ToArray();
    var row = new Row(fields[0], fields[1], fields[2]);

    rows.Add(row);
}

我们从表格的前三列获取数据。

writer.WriteRecords(rows);

最后,记录使用 WriteRecords 写入文件。

来源

CsvHelper Github 页面

在本文中,我们使用 CsvHelper 库在 C# 中读取和写入了 CSV 数据。

作者

我叫 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程