C# 使用 CsvHelper 讀寫 csv 檔案 教學

目錄:

介紹

什麼是 csv? csv 是在 RFC 4180 中描述的一種通用格式,全名是 Comma-Separated Values,逗號分隔值,是一種純文字的通用資料格式,能夠方便的用來傳輸表格資料。

為什麼要使用 CsvHelper? csv 格式看起來很簡單,就簡單寫一寫使用逗號區隔就好了,不過有個大家常常會忽略的小問題,就是內容中出現「,」時會和 csv 的分隔符號衝突,雖然可以自己將內容加上引號「"」,但是就要多寫各種判斷。而 CsvHelper 能夠快速、自動的將類別轉為 csv 格式輸出,省去徒手再造輪子的麻煩。

安裝 CsvHelper

使用 NuGet 或是下面的 .NET CLI 指令安裝 CsvHelper
    
dotnet add package CsvHelper --version 28.0.1
    

輸出 csv 檔案

先建立輸出資料的類別
    
public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
}
    

建立假資料
    
List<Entity> entities = new List<Entity>();
entities.Add(new Entity { Id = 1, Name = "Ruyut"});
entities.Add(new Entity { Id = 2, Name = "小明"});
    

將 list 直接輸出成 csv 檔案
    
using var writer = new StreamWriter("test.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteRecords((IEnumerable)entities);
    

輸出的資料:
    
Id,Name
1,Ruyut
2,小明
    

調整輸出內容的順序

透過在類別上面增加 Index 屬性(Index Attribute) 可以自訂輸出內容的順序(index 從 0 開始)
    
using CsvHelper.Configuration.Attributes;
    
public class Entity
{
    [Index(1)] public int Id { get; set; }
    [Index(0)] public string Name { get; set; }
}
    

輸出的資料:
    
Name,Id
Ruyut,1
小明,2
    

自訂標題名稱

透過在類別上面增加 Name 屬性(Name Attribute) 可以自訂輸出的標題名稱
    
using CsvHelper.Configuration.Attributes;
    
public class Entity
{
    [Name("編號")] public int Id { get; set; }
    [Name("名稱")] public string Name { get; set; }
}
    

輸出的資料:
    
編號,名稱
1,Ruyut
2,小明
    

輸出內容不包含標題

預設輸出的內容會有變數的名稱做標題,如果不想要第一行資料是標題,可以使用 CsvConfiguration 設定
    
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = false };
using var writer = new StreamWriter("test.csv");
using var csv = new CsvWriter(writer, config);
csv.WriteRecords((IEnumerable)entities);
    

輸出的資料:
    
1,Ruyut
2,小明
    

輸出內容接續在原始內容後面

雖然是將內容接續在後面,但是還是會再次輸出標題,所以本次的範例為「接續先前文件且輸出內容不包含標題」
    
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = false };
using var stream = File.Open("test.csv", FileMode.Append);
using var writer = new StreamWriter(stream);
using var csv = new CsvWriter(writer, config);
csv.WriteRecords((IEnumerable)entities);
    

讀取 csv 檔案

範例資料:
    
Name,Id
Ruyut,1
小明,2
    

使用和上面相同的資料類別
    
public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
}
    

讀取資料並顯示:
    
using var reader = new StreamReader("test.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords<Entity>();

foreach (var record in records)
{
    Console.WriteLine($"Id: {record.Id}, Name: {record.Name}");
    // Id: 1, Name: Ruyut
    // Id: 2, Name: 小明
}
    

可以發現只要有標題的情況下順序不同也是能正常讀取的

讀取標題和變數名稱不同的 csv 檔案

當 csv 檔案第一行資料和變數名稱命名不同時,我們可以透過在類別上面增加 Name 屬性(Name Attribute) 可以自訂讀取時該欄位資料放置的變數
    
using CsvHelper.Configuration.Attributes;
    
public class Entity
{
    [Name("編號")] public int Id { get; set; }
    [Name("名稱")] public string Name { get; set; }
}
    

讀取沒有標題的 csv 檔案

在讀取沒有標題的 csv 檔案時會拋出下列錯誤:
    
Unhandled exception. CsvHelper.HeaderValidationException: Header with name 'Id'[0] was not found.
    

讀取方式為:
    
using var reader = new StreamReader("test.csv");
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = false };
using var csv = new CsvReader(reader, config);
var records = csv.GetRecords<Entity>();
    

但此時是依照目標類別的變數順序存放的,我們可以使用 Index 屬性(Index Attribute) 來自定各變數為 csv 檔案中的第幾個資料 (index 從 0 開始)
    
using CsvHelper.Configuration.Attributes;

namespace CsvHelperConsoleAppTest;

public class Entity
{
    [Index(1)] public string Id { get; set; }
    [Index(0)] public string Name { get; set; }
}
    

參考資料


CsvHelper

留言