C# CsvHelperの使い方完全ガイド|CSV読み込み・書き込み・マッピングの実装例を初心者向けに解説

はじめに

C#でCSVファイルを読み込んだり、CSVファイルを書き込んだりする処理は、業務システムや管理画面、バッチ処理、データ移行ツールなどでよく使われます。たとえば、商品一覧をCSVで取り込む、ユーザー情報をCSVで出力する、Excelで作成されたCSVをアプリケーションに読み込む、といった場面です。

C#にはFile.ReadAllLinesStreamReaderなどの標準機能がありますが、CSVにはカンマ、改行、ダブルクォーテーション、文字コード、ヘッダー名、日付形式、空文字など、意外と考慮すべきポイントが多くあります。単純に文字列をカンマで分割するだけでは、実務で使うCSVを安全に処理できないケースが少なくありません。

そこで便利なのが、C#向けのCSV処理ライブラリであるCsvHelperです。CsvHelperを使うと、CSVの読み込み、書き込み、クラスへのマッピング、型変換、ヘッダー設定、区切り文字の変更などをシンプルなコードで実装できます。公式サイトでも、CsvHelperはCSVの読み書きを行う.NETライブラリであり、GetRecords<T>()WriteRecords(records)で簡単に扱えることが紹介されています。joshclose.github.io

この記事では、C#でCsvHelperを使う方法を初心者向けに解説します。CSVの読み込み、書き込み、ClassMapによるマッピング、型変換、エラー対策、ASP.NET Coreでのアップロード処理、大量データの扱いまで、実装例を交えながら順番に見ていきましょう。

1. C#のCsvHelperとは?CSV処理を簡単に実装できるライブラリ

CsvHelperは、C#や.NETでCSVファイルを扱うための定番ライブラリです。CSVファイルを1行ずつ読み取るだけでなく、CSVの各列をC#のクラスプロパティに自動で変換したり、オブジェクトの一覧をCSVとして出力したりできます。

NuGet Galleryでは、CsvHelperは「CSVファイルの読み書きを行うライブラリ」であり、カスタムクラスオブジェクトの読み書きにも対応していると説明されています。2026年6月時点でNuGet上の最新表示はCsvHelper 33.1.0で、.NET Standard 2.0、.NET Framework 4.6.2以上、.NET 8.0以上などに対応しています。NuGet

1-1. CsvHelperでできること

CsvHelperを使うと、主に次のようなCSV処理を実装できます。

C#
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;

たとえば、CSVをクラスに変換して読み込む、クラスの一覧をCSVとして書き出す、ヘッダー名とプロパティ名を対応付ける、日付や数値の形式を指定する、区切り文字をタブやセミコロンに変える、といったことが可能です。

CSVの内容が次のような形式だとします。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25

このCSVを、次のようなC#クラスに変換できます。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}

CsvHelperを使えば、CSVの1行をUserオブジェクト1件として扱えるため、読み込み後の処理がとても書きやすくなります。

1-2. 標準機能でCSVを扱う場合との違い

C#の標準機能だけでCSVを読む場合、次のようなコードを書くことがあります。

C#
var lines = File.ReadAllLines("users.csv");

foreach (var line in lines.Skip(1))
{
var values = line.Split(',');

var user = new User
{
Id = int.Parse(values[0]),
Name = values[1],
Age = int.Parse(values[2])
};
}

一見すると簡単ですが、この方法には問題があります。たとえば、名前にカンマが含まれる場合や、備考欄に改行が含まれる場合、ダブルクォーテーションで囲まれた値がある場合などに正しく処理できません。

csv
Id,Name,Note
1,"Yamada, Taro","東京都在住"
2,"Suzuki Hanako","1行目
2行目"

このようなCSVを単純にSplit(',')で分割すると、列数がずれたり、1レコードが複数行に分かれてしまったりします。CsvHelperを使うと、CSV仕様に沿ってフィールドを解析してくれるため、自前で複雑なパース処理を書く必要が少なくなります。

1-3. CsvHelperが初心者におすすめな理由

CsvHelperが初心者におすすめな理由は、基本的な使い方がわかりやすいからです。CSVの読み込みであればGetRecords<T>()、書き込みであればWriteRecords()を中心に覚えるだけで、基本的な処理を実装できます。

また、クラスとCSVの対応関係を明示するClassMap、日付や数値の変換を制御するTypeConverterOption、ヘッダーの有無や区切り文字を指定するCsvConfigurationなど、実務で必要になりやすい機能もそろっています。公式ドキュメントでも、CsvHelperは高速、柔軟、設定豊富、低メモリ使用などの特徴を持つライブラリとして紹介されています。joshclose.github.io

2. CsvHelperを使うための準備

CsvHelperを使うには、まずNuGetパッケージをインストールします。その後、必要な名前空間を追加し、CsvReaderまたはCsvWriterを使ってCSVを操作します。

2-1. NuGetでCsvHelperをインストールする方法

Visual Studioを使っている場合は、次の手順でインストールできます。

  1. ソリューションエクスプローラーでプロジェクトを右クリックする

  2. 「NuGet パッケージの管理」を選択する

  3. 「参照」タブでCsvHelperを検索する

  4. CsvHelperを選択してインストールする

.NET CLIを使う場合は、ターミナルで次のコマンドを実行します。

Bash
dotnet add package CsvHelper

バージョンを明示したい場合は、次のように指定できます。

Bash
dotnet add package CsvHelper --version 33.1.0

NuGet Gallery上では、CsvHelper 33.1.0が2025年6月2日に更新されたバージョンとして掲載されています。NuGet

2-2. 使用する名前空間と基本コード

CsvHelperを使うときは、主に次の名前空間を使用します。

C#
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;

CSVを読み込む基本形は次のとおりです。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var records = csv.GetRecords<User>().ToList();

CSVを書き込む基本形は次のとおりです。

C#
var records = new List<User>
{
new User { Id = 1, Name = "Taro", Age = 30 },
new User { Id = 2, Name = "Hanako", Age = 25 }
};

using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(records);

CultureInfo.InvariantCultureは、カルチャに依存しない形式でCSVを扱うためによく使われます。日付や小数点などの扱いを安定させたい場合に便利です。

2-3. CSVファイルを扱う前に確認すべき文字コードと区切り文字

CSVを扱う前に、必ず確認しておきたいのが文字コード区切り文字です。

日本語のCSVでは、UTF-8、UTF-8 BOM付き、Shift_JISなどが使われることがあります。特にExcelで開くCSVや、古い業務システムから出力されたCSVではShift_JISが使われることもあります。

また、CSVと呼ばれていても、実際にはカンマではなくタブ、セミコロン、パイプ記号などで区切られている場合もあります。

csv
Id;Name;Age
1;Taro;30
2;Hanako;25

このようなファイルを読み込む場合は、CsvConfigurationで区切り文字を指定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";"
};

using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, config);

var records = csv.GetRecords<User>().ToList();

3. CsvHelperでCSVを読み込む基本方法

ここからは、CsvHelperでCSVを読み込む方法を具体的に見ていきます。まずは最小構成のコードから始め、ヘッダーあり、ヘッダーなし、文字コード指定、エラー対策まで順番に解説します。

3-1. CSVファイルを読み込む最小構成のサンプルコード

次のCSVファイルを読み込む例を考えます。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25

対応するC#クラスを作成します。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}

読み込みコードは次のとおりです。

C#
using CsvHelper;
using System.Globalization;

using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

foreach (var user in users)
{
Console.WriteLine($"{user.Id}: {user.Name} ({user.Age})");
}

GetRecords<User>()を使うと、CSVの各行がUserクラスに変換されます。ヘッダー名とプロパティ名が一致していれば、自動的にマッピングされます。

3-2. ヘッダーありCSVをクラスに変換して読み込む方法

CsvHelperでは、デフォルトで1行目をヘッダーとして扱います。次のようなCSVであれば、IdNameAgeというヘッダー名を見て、同名のプロパティに値を設定します。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25

コードは次のようになります。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

IEnumerable<User> records = csv.GetRecords<User>();

foreach (var record in records)
{
Console.WriteLine(record.Name);
}

注意点として、GetRecords<T>()は遅延実行されます。つまり、foreachで列挙している間にCSVが読み込まれます。usingブロックの外で使いたい場合は、ToList()でリスト化しておくと安全です。

C#
List<User> users;

using (var reader = new StreamReader("users.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
users = csv.GetRecords<User>().ToList();
}

3-3. ヘッダーなしCSVを読み込む方法

ヘッダーがないCSVを読み込む場合は、HasHeaderRecord = falseを指定します。

csv
1,Taro,30
2,Hanako,25

この場合、列名がないため、列順でプロパティにマッピングします。ClassMapでインデックスを指定するのがおすすめです。

C#
using CsvHelper.Configuration;

public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Index(0);
Map(m => m.Name).Index(1);
Map(m => m.Age).Index(2);
}
}

読み込みコードは次のようになります。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, config);

csv.Context.RegisterClassMap<UserMap>();

var users = csv.GetRecords<User>().ToList();

ヘッダーなしCSVでは、列の順番が変わると読み込み結果も変わるため、CSV仕様を固定しておくことが重要です。

3-4. 文字化けを防ぐエンコーディング指定

日本語を含むCSVでは、文字コードを明示しないと文字化けすることがあります。UTF-8のCSVを読み込む場合は、次のように指定します。

C#
using var reader = new StreamReader("users.csv", Encoding.UTF8);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

Shift_JISのCSVを読み込む場合は、.NET Coreや.NET 5以降ではコードページプロバイダーの登録が必要になることがあります。

C#
using System.Text;

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

var encoding = Encoding.GetEncoding("shift_jis");

using var reader = new StreamReader("users.csv", encoding);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

Shift_JISを扱う場合は、プロジェクトにSystem.Text.Encoding.CodePagesパッケージが必要になることがあります。

Bash
dotnet add package System.Text.Encoding.CodePages

文字化けが起きる場合は、CSVファイルの実際の文字コードと、StreamReaderで指定している文字コードが一致しているか確認しましょう。

3-5. 読み込み時によくあるエラーと対処法

CSV読み込み時によくあるエラーには、ヘッダー名が見つからない、型変換に失敗する、列数が足りない、文字化けする、というものがあります。

たとえば、CSVのヘッダーがUserNameなのに、C#のプロパティがNameの場合、自動マッピングでは一致しません。

csv
Id,UserName,Age
1,Taro,30

この場合は、ClassMapで明示的に対応付けます。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("UserName");
Map(m => m.Age).Name("Age");
}
}

読み込み時に登録します。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<UserMap>();

var users = csv.GetRecords<User>().ToList();

CSVの形式が不安定な場合は、MissingFieldFoundHeaderValidatednullにして、厳密な検証を緩めることもできます。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
MissingFieldFound = null,
HeaderValidated = null
};

ただし、エラーを無視すると不正なデータを見逃す可能性があります。実務では、無視するだけでなくログ出力やエラー行の記録も行うと安全です。

4. CsvHelperでCSVを書き込む基本方法

CsvHelperでは、C#のオブジェクト一覧をCSVファイルとして簡単に出力できます。管理画面からCSVダウンロード機能を作る場合や、バッチ処理でCSVを生成する場合に便利です。

4-1. Listや配列のデータをCSVに出力する方法

次のようなUserクラスがあるとします。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}

List<User>をCSVに出力するコードは次のとおりです。

C#
var users = new List<User>
{
new User { Id = 1, Name = "Taro", Age = 30 },
new User { Id = 2, Name = "Hanako", Age = 25 }
};

using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

出力されるCSVは次のようになります。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25

CsvHelperでは、デフォルトでプロパティ名をヘッダーとして出力します。

4-2. ヘッダー付きCSVを書き込む方法

WriteRecords()を使うと、通常はヘッダー付きでCSVが出力されます。より明示的にヘッダーとレコードを書きたい場合は、次のように書くこともできます。

C#
using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteHeader<User>();
csv.NextRecord();

foreach (var user in users)
{
csv.WriteRecord(user);
csv.NextRecord();
}

この方法は、ヘッダーを書いたあとに1件ずつレコードを書き込みたい場合に便利です。大量データをストリーム処理で書き出す場合にも使いやすい書き方です。

4-3. 追記モードでCSVに書き込む方法

既存のCSVファイルにデータを追記したい場合は、StreamWriterの第2引数にtrueを指定します。

C#
using var writer = new StreamWriter("users.csv", append: true);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

ただし、このままだと追記時にもヘッダーが出力される場合があります。既存ファイルに追記する場合は、ヘッダーを出さない設定にするとよいでしょう。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

using var writer = new StreamWriter("users.csv", append: true);
using var csv = new CsvWriter(writer, config);

csv.WriteRecords(users);

新規作成時はヘッダーあり、追記時はヘッダーなし、というように処理を分けるのが実務ではよく使われます。

4-4. UTF-8やShift_JISでCSVを出力する方法

UTF-8でCSVを出力する場合は、StreamWriterにエンコーディングを指定します。

C#
using var writer = new StreamWriter("users.csv", false, Encoding.UTF8);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

Excelで開くことを想定してBOM付きUTF-8にしたい場合は、次のように指定します。

C#
var utf8Bom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);

using var writer = new StreamWriter("users.csv", false, utf8Bom);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

Shift_JISで出力する場合は、読み込み時と同じようにコードページプロバイダーを登録します。

C#
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

var shiftJis = Encoding.GetEncoding("shift_jis");

using var writer = new StreamWriter("users.csv", false, shiftJis);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

4-5. 改行・カンマ・ダブルクォーテーションを含むデータの扱い

CSVでは、値の中にカンマ、改行、ダブルクォーテーションが含まれる場合があります。

C#
var users = new List<UserNote>
{
new UserNote
{
Id = 1,
Name = "Taro",
Note = "東京都,大阪府"
},
new UserNote
{
Id = 2,
Name = "Hanako",
Note = "1行目\n2行目"
},
new UserNote
{
Id = 3,
Name = "Jiro",
Note = "彼は\"OK\"と言いました"
}
};

対応するクラスは次のとおりです。

C#
public class UserNote
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Note { get; set; } = "";
}

CsvHelperで書き込むと、必要に応じてダブルクォーテーションで囲むなどの処理を行ってくれます。

C#
using var writer = new StreamWriter("users.csv", false, Encoding.UTF8);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

単純な文字列連結でCSVを作ると、このような値を正しくエスケープする必要があります。CsvHelperを使う大きなメリットのひとつは、このようなCSV特有の面倒な処理を任せられることです。

5. CsvHelperのマッピング機能の使い方

CsvHelperのマッピング機能を使うと、CSVの列とC#クラスのプロパティの対応関係を細かく制御できます。プロパティ名とヘッダー名が異なる場合、日本語ヘッダーを使う場合、列順で読み込む場合、不要な列を無視する場合などに便利です。

5-1. クラスプロパティとCSV列を自動マッピングする方法

CSVのヘッダー名とC#のプロパティ名が一致している場合、CsvHelperは自動的にマッピングします。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25
C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}

読み込みコードはシンプルです。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

このように、ヘッダー名とプロパティ名をそろえておくと、マッピング設定を書かずに読み込めます。

5-2. ClassMapを使って列名を明示的に指定する方法

CSVのヘッダー名とプロパティ名が異なる場合は、ClassMapを使います。

csv
user_id,user_name,user_age
1,Taro,30
2,Hanako,25

C#クラスは次のように通常のプロパティ名にします。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}

ClassMapを作成します。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("user_id");
Map(m => m.Name).Name("user_name");
Map(m => m.Age).Name("user_age");
}
}

読み込み時に登録します。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<UserMap>();

var users = csv.GetRecords<User>().ToList();

書き込み時にも同じClassMapを登録すれば、指定したヘッダー名でCSVを出力できます。

C#
using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<UserMap>();
csv.WriteRecords(users);

5-3. CSVの列順でマッピングする方法

ヘッダーがないCSVや、列名ではなく列順で処理したいCSVでは、Index()を使って列番号を指定します。

C#
public sealed class UserIndexMap : ClassMap<User>
{
public UserIndexMap()
{
Map(m => m.Id).Index(0);
Map(m => m.Name).Index(1);
Map(m => m.Age).Index(2);
}
}

ヘッダーなしCSVを読み込む場合は、HasHeaderRecord = falseも設定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, config);

csv.Context.RegisterClassMap<UserIndexMap>();

var users = csv.GetRecords<User>().ToList();

列順でマッピングする場合は、CSVの列順が変わらないことが前提です。外部システムから受け取るCSVでは、事前に仕様書などで列順を確認しておきましょう。

5-4. プロパティ名とヘッダー名が異なる場合の対応

ヘッダー名が日本語の場合も、ClassMapで対応できます。

csv
ユーザーID,氏名,年齢
1,山田太郎,30
2,佐藤花子,25
C#
public sealed class JapaneseUserMap : ClassMap<User>
{
public JapaneseUserMap()
{
Map(m => m.Id).Name("ユーザーID");
Map(m => m.Name).Name("氏名");
Map(m => m.Age).Name("年齢");
}
}

このように、C#側のプロパティ名は英語のままにして、CSVのヘッダー名だけを日本語に対応させることができます。

C#
using var reader = new StreamReader("users.csv", Encoding.UTF8);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<JapaneseUserMap>();

var users = csv.GetRecords<User>().ToList();

日本語ヘッダーを扱う場合は、文字コードにも注意しましょう。

5-5. 不要な列を無視する方法

CSVに不要な列が含まれている場合でも、必要な列だけをマッピングできます。

csv
Id,Name,Age,UnusedColumn
1,Taro,30,xxx
2,Hanako,25,yyy

C#クラスにUnusedColumnがなくても、必要な列だけを指定すれば読み込めます。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("Name");
Map(m => m.Age).Name("Age");
}
}

逆に、C#クラスには存在するがCSVに出力したくないプロパティがある場合は、Ignore()を使います。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
public string InternalMemo { get; set; } = "";
}

public sealed class UserWriteMap : ClassMap<User>
{
public UserWriteMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("Name");
Map(m => m.Age).Name("Age");
Map(m => m.InternalMemo).Ignore();
}
}

公式ドキュメントの例でも、ClassMapにはプロパティのマッピング、名前によるマッピング、インデックスによるマッピング、プロパティの無視などの設定項目が用意されています。joshclose.github.io

6. CsvHelperで型変換を行う方法

CSVの値は基本的に文字列ですが、C#ではintdecimalDateTimeboolなどの型として扱いたいことが多くあります。CsvHelperは、CSVの文字列をC#の型に変換する機能を持っています。

6-1. 数値・日付・bool型を読み込む方法

次のようなCSVを読み込む例を考えます。

csv
Id,Name,Price,CreatedAt,IsActive
1,Apple,120.5,2026-06-01,true
2,Orange,98.0,2026-06-02,false

対応するクラスは次のとおりです。

C#
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; }
public bool IsActive { get; set; }
}

CSVの値が標準的な形式であれば、そのまま読み込めます。

C#
using var reader = new StreamReader("products.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var products = csv.GetRecords<Product>().ToList();

intdecimalDateTimeboolに変換できない値が入っていると、型変換エラーが発生します。

6-2. 日付フォーマットを指定する方法

CSVの日付がyyyy/MM/dd形式の場合は、ClassMapで日付フォーマットを指定できます。

csv
Id,Name,CreatedAt
1,Taro,2026/06/01
2,Hanako,2026/06/02
C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public DateTime CreatedAt { get; set; }
}
C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("Name");
Map(m => m.CreatedAt).Name("CreatedAt")
.TypeConverterOption.Format("yyyy/MM/dd");
}
}

読み込みコードは次のとおりです。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<UserMap>();

var users = csv.GetRecords<User>().ToList();

CsvHelperでは、型変換オプションを使って日付や数値などの変換設定を指定できます。公式ドキュメントでも、型変換オプションはIFormattable.ToStringTryParseで使用される設定を渡せる仕組みとして説明されています。joshclose.github.io

6-3. nullや空文字を扱う方法

CSVでは、値が空の列がよくあります。

csv
Id,Name,Birthday
1,Taro,1990/01/01
2,Hanako,

DateTimeはnullを許容しないため、空文字を読み込むと変換エラーになることがあります。空を許可したい場合は、DateTime?のようにnullable型にします。

C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public DateTime? Birthday { get; set; }
}

さらに、空文字や特定の文字列をnullとして扱いたい場合は、NullValues()を指定します。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("Name");
Map(m => m.Birthday).Name("Birthday")
.TypeConverterOption.Format("yyyy/MM/dd")
.TypeConverterOption.NullValues("");
}
}

NULLN/Aなどをnull扱いにしたい場合は、次のように指定できます。

C#
.TypeConverterOption.NullValues("", "NULL", "N/A");

6-4. 独自の型変換を実装する方法

CSVの値を独自ルールで変換したい場合は、カスタム型コンバーターを作成できます。たとえば、CSV上では有効無効と書かれている値をboolに変換する例です。

csv
Id,Name,Status
1,Taro,有効
2,Hanako,無効
C#
public class UserStatus
{
public int Id { get; set; }
public string Name { get; set; } = "";
public bool IsActive { get; set; }
}

カスタムコンバーターを作成します。

C#
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;

public class JapaneseBoolConverter : DefaultTypeConverter
{
public override object ConvertFromString(
string? text,
IReaderRow row,
MemberMapData memberMapData)
{
return text switch
{
"有効" => true,
"無効" => false,
_ => throw new TypeConverterException(
this,
memberMapData,
text,
row.Context,
"有効または無効を指定してください。")
};
}

public override string ConvertToString(
object? value,
IWriterRow row,
MemberMapData memberMapData)
{
return value is bool boolValue && boolValue ? "有効" : "無効";
}
}

ClassMapで指定します。

C#
public sealed class UserStatusMap : ClassMap<UserStatus>
{
public UserStatusMap()
{
Map(m => m.Id).Name("Id");
Map(m => m.Name).Name("Name");
Map(m => m.IsActive).Name("Status")
.TypeConverter<JapaneseBoolConverter>();
}
}

読み込みコードです。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<UserStatusMap>();

var users = csv.GetRecords<UserStatus>().ToList();

6-5. 型変換エラーが発生した場合の対処法

型変換エラーは、CSVの値がC#の型に変換できないときに発生します。たとえば、int型の列にabcが入っている場合です。

csv
Id,Name,Age
1,Taro,30
2,Hanako,abc

この場合、Ageintに変換できないためエラーになります。対処法としては、次のような方法があります。

まず、CSVの値を修正できるなら、正しい値に直すのが基本です。

csv
2,Hanako,25

値が空になる可能性があるなら、nullable型にします。

C#
public int? Age { get; set; }

不正な値をログに残して処理を続けたい場合は、1件ずつ読み込みながら例外処理を行います。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Read();
csv.ReadHeader();

var users = new List<User>();

while (csv.Read())
{
try
{
var user = csv.GetRecord<User>();
users.Add(user);
}
catch (Exception ex)
{
Console.WriteLine($"CSV読み込みエラー: {ex.Message}");
Console.WriteLine($"行番号: {csv.Context.Parser.Row}");
}
}

実務では、エラー行だけを別ファイルに出力したり、画面に「何行目のどの列が不正です」と表示したりすると、運用しやすくなります。

7. CsvHelperの設定をカスタマイズする方法

CsvHelperでは、CsvConfigurationを使って読み込みや書き込みの動作をカスタマイズできます。区切り文字、ヘッダーの有無、カルチャ情報、不正データの扱いなどを指定できます。

7-1. 区切り文字をカンマ以外に変更する方法

タブ区切りやセミコロン区切りのファイルを扱う場合は、Delimiterを指定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "\t"
};

using var reader = new StreamReader("users.tsv");
using var csv = new CsvReader(reader, config);

var users = csv.GetRecords<User>().ToList();

セミコロン区切りの場合は次のようにします。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";"
};

パイプ区切りの場合は次のようにします。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "|"
};

7-2. ヘッダーの有無を設定する方法

ヘッダーがあるCSVでは、通常は何も指定しなくても読み込めます。ヘッダーがないCSVでは、HasHeaderRecord = falseを指定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

書き込み時にヘッダーを出力したくない場合にも、この設定を使います。

C#
using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, config);

csv.WriteRecords(users);

ヘッダーなしCSVでは列名で判断できないため、Index()を使ったマッピングと組み合わせるのがおすすめです。

7-3. カルチャ情報を指定する方法

CsvHelperでは、CultureInfoによって日付や数値の扱いが変わる場合があります。一般的なCSV処理では、CultureInfo.InvariantCultureを使うことが多いです。

C#
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

日本のカルチャに合わせたい場合は、次のように指定できます。

C#
var culture = new CultureInfo("ja-JP");

using var csv = new CsvReader(reader, culture);

小数点や日付形式が国や地域によって異なる場合は、CSVを作成する側と読み込む側でカルチャ設定をそろえておくことが大切です。

7-4. 大文字・小文字や空白を無視してヘッダーを読み込む方法

CSVのヘッダーに余分な空白があったり、大文字・小文字がそろっていなかったりする場合は、PrepareHeaderForMatchを使って比較前のヘッダー文字列を整形できます。

csv
 id , NAME , age
1,Taro,30
2,Hanako,25

次のように設定すると、ヘッダー名をトリムして小文字化してからマッピングできます。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
PrepareHeaderForMatch = args => args.Header.Trim().ToLowerInvariant()
};

using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, config);

var users = csv.GetRecords<User>().ToList();

プロパティ名側も同じように比較されるため、IdidNamenameのような違いを吸収できます。

7-5. 不正なデータや欠損列を無視する方法

CSVによっては、列が不足していたり、余分な列があったり、ヘッダーが想定と違っていたりすることがあります。厳密にエラーにしたい場合もあれば、できるだけ読み込みを続けたい場合もあります。

欠損フィールドを無視したい場合は、次のように設定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
MissingFieldFound = null
};

ヘッダー検証エラーを無視したい場合は、次のように設定します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HeaderValidated = null
};

不正データを検知したい場合は、BadDataFoundを使ってログを出すこともできます。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
BadDataFound = args =>
{
Console.WriteLine($"不正なデータ: {args.RawRecord}");
}
};

ただし、不正データを無視しすぎると、後続処理で予期しない不具合が起きる可能性があります。重要なCSVでは、エラー行を記録して利用者に修正を促す設計にしましょう。

8. CsvHelperの実践的な実装例

ここでは、実務で使いやすいCsvHelperの実装例を紹介します。データベース登録、CSVダウンロード、ASP.NET Coreでのアップロード、大量データ処理、非同期処理を見ていきます。

8-1. CSVを読み込んでデータベース登録用のオブジェクトに変換する

CSVを読み込んで、データベース登録用のオブジェクトに変換する例です。

csv
商品コード,商品名,価格
A001,りんご,120
A002,みかん,100

CSV読み込み用クラスを作成します。

C#
public class ProductCsvRow
{
public string Code { get; set; } = "";
public string Name { get; set; } = "";
public decimal Price { get; set; }
}

マッピングを定義します。

C#
public sealed class ProductCsvMap : ClassMap<ProductCsvRow>
{
public ProductCsvMap()
{
Map(m => m.Code).Name("商品コード");
Map(m => m.Name).Name("商品名");
Map(m => m.Price).Name("価格");
}
}

読み込み処理です。

C#
using var reader = new StreamReader("products.csv", Encoding.UTF8);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<ProductCsvMap>();

var rows = csv.GetRecords<ProductCsvRow>().ToList();

データベース登録用のエンティティに変換します。

C#
var products = rows.Select(row => new ProductEntity
{
Code = row.Code,
Name = row.Name,
Price = row.Price,
CreatedAt = DateTime.UtcNow
}).ToList();

読み込み用クラスとDBエンティティを分けることで、CSV仕様の変更があっても影響範囲を小さくできます。

8-2. オブジェクトの一覧をCSVとしてダウンロード用に出力する

ASP.NET CoreなどでCSVダウンロード機能を作る場合は、MemoryStreamStringWriterを使ってCSV文字列を生成できます。

C#
public class UserCsvRow
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}

CSV文字列を作る例です。

C#
var users = new List<UserCsvRow>
{
new UserCsvRow { Id = 1, Name = "Taro", Email = "taro@example.com" },
new UserCsvRow { Id = 2, Name = "Hanako", Email = "hanako@example.com" }
};

using var writer = new StringWriter();
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

var csvText = writer.ToString();

ASP.NET Coreのコントローラーで返す場合は、次のようなイメージです。

C#
[HttpGet("download")]
public IActionResult Download()
{
var users = new List<UserCsvRow>
{
new UserCsvRow { Id = 1, Name = "Taro", Email = "taro@example.com" },
new UserCsvRow { Id = 2, Name = "Hanako", Email = "hanako@example.com" }
};

using var writer = new StringWriter();
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

var bytes = Encoding.UTF8.GetBytes(writer.ToString());

return File(bytes, "text/csv", "users.csv");
}

Excelでの文字化けを避けたい場合は、BOM付きUTF-8で出力することも検討しましょう。

8-3. ASP.NET CoreでCSVアップロードを処理する

ASP.NET CoreでCSVファイルをアップロードし、CsvHelperで読み込む例です。

C#
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0)
{
return BadRequest("CSVファイルを選択してください。");
}

using var stream = file.OpenReadStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<ProductCsvMap>();

var rows = csv.GetRecords<ProductCsvRow>().ToList();

foreach (var row in rows)
{
// バリデーションやDB登録処理を行う
}

return Ok($"{rows.Count}件のCSVを読み込みました。");
}

アップロード処理では、ファイルサイズ、拡張子、Content-Type、文字コード、CSVの列数、必須項目、数値や日付の形式などをチェックしましょう。

8-4. 大量データをメモリ効率よく読み込む

大量データを扱う場合、ToList()で全件をメモリに読み込むとメモリ使用量が大きくなります。CsvHelperのGetRecords<T>()は列挙しながら読み込めるため、1件ずつ処理することでメモリ効率を高められます。公式サイトでも、レコード読み込みはyieldによって処理され、一度に1レコードだけをメモリに保持できることが特徴として紹介されています。joshclose.github.io

C#
using var reader = new StreamReader("large_users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

foreach (var user in csv.GetRecords<User>())
{
// 1件ずつ処理する
Console.WriteLine(user.Name);

// 例: 一定件数ごとにDBへ保存する
}

大量データでは、次の点を意識しましょう。

C#
var buffer = new List<User>();
const int batchSize = 1000;

foreach (var user in csv.GetRecords<User>())
{
buffer.Add(user);

if (buffer.Count >= batchSize)
{
// DBへ一括登録する
// SaveUsers(buffer);

buffer.Clear();
}
}

if (buffer.Count > 0)
{
// 残りを登録する
// SaveUsers(buffer);
}

全件をメモリに保持せず、バッチ単位で処理するのがポイントです。

8-5. 非同期処理でCSVを読み書きする

CsvHelperには非同期で読み書きするためのメソッドもあります。ASP.NET CoreなどでファイルI/Oを非同期化したい場合に便利です。

非同期で読み込む例です。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = new List<User>();

await foreach (var user in csv.GetRecordsAsync<User>())
{
users.Add(user);
}

非同期で書き込む例です。

C#
using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

await csv.WriteRecordsAsync(users);

大量データを非同期で処理する場合も、全件をリスト化せず、1件ずつまたは一定件数ごとに処理するとメモリ効率がよくなります。

9. CsvHelperでよくあるエラーと解決策

CsvHelperを使っていると、ヘッダー名の不一致、型変換エラー、列不足、文字化け、列数のずれなどに遭遇することがあります。ここでは、よくあるエラーと解決策を紹介します。

9-1. Header with name was not foundの原因と対処法

Header with name 'xxx' was not foundは、CsvHelperが期待しているヘッダー名をCSV内で見つけられなかったときに発生します。

原因として多いのは、CSVのヘッダー名とプロパティ名が一致していないケースです。

csv
user_id,user_name
1,Taro
C#
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

この場合、IdNameというヘッダーが存在しないため、自動マッピングに失敗します。解決策はClassMapでヘッダー名を明示することです。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("user_id");
Map(m => m.Name).Name("user_name");
}
}

また、ヘッダーに空白が含まれている場合は、PrepareHeaderForMatchで整形します。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
PrepareHeaderForMatch = args => args.Header.Trim()
};

9-2. TypeConverterExceptionの原因と対処法

TypeConverterExceptionは、CSVの文字列をC#の型に変換できないときに発生します。

たとえば、int型の列に文字列が入っている場合です。

csv
Id,Age
1,30
2,abc

解決策としては、CSVデータを修正する、nullable型にする、独自コンバーターを使う、読み込み時に例外を捕捉する、などがあります。

C#
public int? Age { get; set; }

日付形式が原因の場合は、フォーマットを指定します。

C#
Map(m => m.CreatedAt)
.TypeConverterOption.Format("yyyy/MM/dd");

エラー内容をログに出したい場合は、1件ずつ読み込みながら例外処理します。

C#
while (csv.Read())
{
try
{
var record = csv.GetRecord<User>();
}
catch (TypeConverterException ex)
{
Console.WriteLine($"型変換エラー: {ex.Message}");
Console.WriteLine($"行番号: {csv.Context.Parser.Row}");
}
}

9-3. MissingFieldFoundエラーの原因と対処法

MissingFieldFoundに関連するエラーは、CSVに必要な列が存在しない場合や、列数が不足している場合に発生します。

csv
Id,Name,Age
1,Taro,30
2,Hanako

2行目ではAge列の値が不足しています。このようなCSVを許容したい場合は、次のように設定できます。

C#
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
MissingFieldFound = null
};

ただし、列不足を無視すると、データの欠落に気づきにくくなります。重要なデータでは、無視するよりもエラーとして扱い、利用者にCSV修正を促すほうが安全です。

9-4. 文字化けする場合の確認ポイント

文字化けする場合は、次のポイントを確認します。

まず、CSVファイルの文字コードを確認します。UTF-8なのか、BOM付きUTF-8なのか、Shift_JISなのかを確認しましょう。

UTF-8の場合は次のように指定します。

C#
using var reader = new StreamReader("users.csv", Encoding.UTF8);

Shift_JISの場合は次のようにします。

C#
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

using var reader = new StreamReader(
"users.csv",
Encoding.GetEncoding("shift_jis"));

次に、Excelで開くことを想定している場合は、BOM付きUTF-8またはShift_JISを検討します。

C#
var utf8Bom = new UTF8Encoding(true);

using var writer = new StreamWriter("users.csv", false, utf8Bom);

文字化けはCsvHelperそのものの問題ではなく、StreamReaderStreamWriterで指定する文字コードとCSVファイルの実際の文字コードが合っていないことが原因であるケースが多いです。

9-5. CSVの列数が合わない場合の対応

CSVの列数が行によって異なる場合、読み込みエラーやデータずれの原因になります。

csv
Id,Name,Age
1,Taro,30
2,Hanako,25,Extra
3,Jiro

このようなCSVでは、余分な列や不足している列があるため、事前にチェックするのがおすすめです。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

csv.Read();
csv.ReadHeader();

while (csv.Read())
{
var columnCount = csv.Parser.Count;

if (columnCount != 3)
{
Console.WriteLine($"列数エラー 行番号: {csv.Context.Parser.Row}");
continue;
}

var user = csv.GetRecord<User>();
}

外部から受け取るCSVでは、いきなりDB登録するのではなく、まずバリデーションを行い、エラーがあれば一覧で返す設計にすると運用しやすくなります。

10. CsvHelperを使うときのベストプラクティス

CsvHelperは簡単に使えるライブラリですが、実務で安定して運用するには設計上の工夫が必要です。ここでは、C#でCsvHelperを使うときのベストプラクティスを紹介します。

10-1. 読み込み用・書き込み用クラスを分ける

CSV読み込み用のクラスと、データベースのエンティティや画面表示用のDTOは分けるのがおすすめです。

C#
public class ProductCsvRow
{
public string Code { get; set; } = "";
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
C#
public class ProductEntity
{
public int Id { get; set; }
public string Code { get; set; } = "";
public string Name { get; set; } = "";
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; }
}

CSV仕様とDB設計を直接結びつけると、CSVの列名が変わっただけでDB側のクラスにも影響が出ることがあります。CSV専用クラスを用意しておくと、仕様変更に対応しやすくなります。

10-2. ClassMapでマッピングを明示する

ヘッダー名とプロパティ名が完全に一致している場合は自動マッピングでも問題ありません。しかし、実務ではCSVのヘッダー名が日本語だったり、外部システム由来の名前だったりすることが多くあります。

そのため、重要なCSV処理ではClassMapでマッピングを明示するのがおすすめです。

C#
public sealed class ProductCsvMap : ClassMap<ProductCsvRow>
{
public ProductCsvMap()
{
Map(m => m.Code).Name("商品コード");
Map(m => m.Name).Name("商品名");
Map(m => m.Price).Name("価格");
}
}

ClassMapを使うことで、CSV仕様をコード上で明確に表現できます。

10-3. CSV仕様を事前に決めておく

CSV処理を安定させるには、事前にCSV仕様を決めておくことが重要です。少なくとも、次の項目は明確にしておきましょう。

・文字コード
・区切り文字
・ヘッダーの有無
・列名
・列順
・必須項目
・日付形式
・数値形式
・空文字やnullの扱い
・改行やカンマを含む値の扱い

CSV仕様が曖昧なまま実装すると、読み込みエラーや文字化け、列ずれ、型変換エラーが起きやすくなります。

10-4. 例外処理とログ出力を実装する

CSV読み込みでは、利用者が作成したファイルや外部システムから受け取ったファイルを扱うことが多いため、必ず例外処理を実装しましょう。

C#
try
{
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();
}
catch (HeaderValidationException ex)
{
Console.WriteLine($"ヘッダーエラー: {ex.Message}");
}
catch (TypeConverterException ex)
{
Console.WriteLine($"型変換エラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"CSV読み込みエラー: {ex.Message}");
}

実務では、コンソール出力ではなく、アプリケーションのロガーを使って記録しましょう。ASP.NET CoreであればILoggerを使うのが一般的です。

C#
_logger.LogError(ex, "CSV読み込み中にエラーが発生しました。");

10-5. 大量データではストリーム処理を使う

大量CSVを扱う場合は、ToList()で全件をメモリに読み込まないようにしましょう。

C#
foreach (var record in csv.GetRecords<User>())
{
// 1件ずつ処理する
}

DB登録を行う場合は、一定件数ごとにバッチ登録するのがおすすめです。

C#
var buffer = new List<User>();
const int batchSize = 1000;

foreach (var user in csv.GetRecords<User>())
{
buffer.Add(user);

if (buffer.Count == batchSize)
{
// BulkInsert(buffer);
buffer.Clear();
}
}

if (buffer.Any())
{
// BulkInsert(buffer);
}

ファイルサイズが大きい場合は、処理時間、メモリ使用量、タイムアウト、DB負荷も考慮しましょう。

11. C# CsvHelperに関するよくある質問

最後に、C#でCsvHelperを使うときによくある質問をまとめます。

11-1. CsvHelperは無料で使える?

CsvHelperはオープンソースのライブラリで、公式サイトでは商用利用も無料であり、MS-PLとApache 2.0のデュアルライセンスで提供されていると説明されています。joshclose.github.io

ただし、実際のプロジェクトで利用する場合は、自社や案件のライセンス確認ルールに従って確認してください。

11-2. Shift_JISのCSVは読み込める?

はい、読み込めます。StreamReaderでShift_JISのエンコーディングを指定します。

C#
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

using var reader = new StreamReader(
"users.csv",
Encoding.GetEncoding("shift_jis"));

using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

.NET Coreや.NET 5以降では、System.Text.Encoding.CodePagesパッケージが必要になる場合があります。

11-3. Excelで開けるCSVを出力できる?

はい、出力できます。Excelで日本語が文字化けしないようにしたい場合は、BOM付きUTF-8またはShift_JISで出力する方法があります。

BOM付きUTF-8の例です。

C#
var utf8Bom = new UTF8Encoding(true);

using var writer = new StreamWriter("users.csv", false, utf8Bom);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

Shift_JISの例です。

C#
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

var shiftJis = Encoding.GetEncoding("shift_jis");

using var writer = new StreamWriter("users.csv", false, shiftJis);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

どちらを使うかは、利用者のExcel環境や既存システムの仕様に合わせて決めましょう。

11-4. ヘッダー名が日本語でも使える?

はい、日本語ヘッダーでも使えます。ClassMapで日本語の列名を指定します。

csv
ユーザーID,氏名,年齢
1,山田太郎,30
C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("ユーザーID");
Map(m => m.Name).Name("氏名");
Map(m => m.Age).Name("年齢");
}
}

日本語ヘッダーを扱う場合は、文字コードの指定も忘れないようにしましょう。

11-5. CsvHelperとFile.ReadAllLinesはどちらを使うべき?

単純なテキストファイルを1行ずつ読むだけなら、File.ReadAllLinesでも十分です。しかし、CSVとして正しく扱いたい場合はCsvHelperを使うのがおすすめです。

特に、次のようなケースではCsvHelperが向いています。

・カンマを含む値がある
・改行を含む値がある
・ダブルクォーテーションを含む値がある
・ヘッダー名とクラスをマッピングしたい
・数値や日付に型変換したい
・Shift_JISやUTF-8など文字コードを考慮したい
・大量データをストリーム処理したい
・CSVの読み書きを保守しやすくしたい

逆に、CSVではなく単純な1行1データのファイルであれば、File.ReadAllLinesのほうが簡単な場合もあります。CSV仕様を意識する必要があるなら、CsvHelperを使うと安全です。

まとめ

C#でCSVを扱うなら、CsvHelperを使うことで読み込み、書き込み、マッピング、型変換、エラー処理を効率よく実装できます。

基本的な読み込みは、CsvReaderGetRecords<T>()を使います。

C#
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var users = csv.GetRecords<User>().ToList();

基本的な書き込みは、CsvWriterWriteRecords()を使います。

C#
using var writer = new StreamWriter("users.csv");
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.WriteRecords(users);

ヘッダー名とプロパティ名が異なる場合、日本語ヘッダーを使う場合、列順で読み込む場合は、ClassMapを使ってマッピングを明示しましょう。

C#
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("ユーザーID");
Map(m => m.Name).Name("氏名");
Map(m => m.Age).Name("年齢");
}
}

また、日本語CSVでは文字コードにも注意が必要です。UTF-8、BOM付きUTF-8、Shift_JISのどれを使うのかを事前に決め、StreamReaderStreamWriterで正しく指定しましょう。

CsvHelperは、単純なCSV処理から実務レベルのCSVアップロード、CSVダウンロード、大量データ処理まで幅広く対応できます。C#でCSV処理を実装する場合は、まずCsvHelperを導入し、CsvConfigurationClassMap、型変換、例外処理を組み合わせて、保守しやすく安全なCSV処理を作るのがおすすめです。