CSVファイルを扱う案件が増加する中で、C#でCSVファイルを**正確にかつ効率的に読み込む方法**を知りたい方が多いです。単に1行ずつSplitで処理するだけではダブルクォーテーション内のカンマや改行で失敗することがあります。この記事では標準機能やライブラリを使った実践的な手法を豊富なコード例と共に紹介し、「C# CSV ファイル 読み込み」の疑問点を徹底解消します。
C# CSV ファイル 読み込み の基本: 標準機能で配列またはリストに格納する方法
まず最初に、外部ライブラリを使わずに標準機能だけで**CSVファイルを読み込み、配列またはリストにデータを格納する手法**を解説します。小〜中規模のファイルや簡単なフォーマットの場合にはこれで十分ですが、仕様の曖昧さやフォーマットの揺らぎに注意が必要です。標準機能とC#の言語・クラス(StreamReader, String.Split など)を使うパターンをステップごとに見ていきます。
StreamReader と String.Split を使った1行ずつの読み込み
標準的な方法は、StreamReader クラスでファイルを開き、ReadLine で 1 行ずつ読み込んで String.Split で区切るパターンです。以下は基本的な例です。
using System.IO;
using System.Text;
…
using(var sr = new StreamReader(path, Encoding.UTF8))
{
while(!sr.EndOfStream)
{
string line = sr.ReadLine();
string[] cols = line.Split(',');
// cols を配列またはリストに格納
}
}
この方法は非常に簡単ですが、データ中にカンマ・改行・ダブルクォーテーションが入る場合に正しく動作しないことがあります。
また文字コードが UTF-8/Shift_JIS など異なる場合に文字化けが起きることがあり、Encoding 指定を忘れないことが重要です。標準機能のみで処理する際の限界と共に、使用できる条件(データに特殊文字が含まれない、小さめのファイルなど)を把握しておくことが望ましいです。
Microsoft.VisualBasic.FileIO.TextFieldParser を用いた安全な読み込み
標準機能でありながら、少し高度な CSV フォーマット(RFC 4180 準拠風)を扱いたい場合に TextFieldParser クラスは有効です。このクラスはカンマや他の区切り文字、引用符で囲まれたフィールド、空行スキップなどを含む仕様を持っており、より信頼性の高い読み込みを実現できます。
TextFieldParser を使ったサンプルコード例:
using Microsoft.VisualBasic.FileIO;
…
using(var parser = new TextFieldParser(path, Encoding.UTF8))
{
parser.Delimiters = new string[] { "," };
while(!parser.EndOfData)
{
string[] fields = parser.ReadFields();
// fields を配列またはリストに格納
}
}
この方法は Windows (.NET Framework) で特に使われますが、.NET Core/.NET 5+ 環境では Microsoft.VisualBasic.Core パッケージを導入する必要があるケースがあります。
String.Split の限界とエスケープ処理の必要性
String.Split は扱いやすい反面、仕様的な誤処理をしがちです。例えば、フィールド内にカンマが含まれていたら、それが区切りと誤認されます。また、ダブルクォーテーションで囲われたフィールド内の改行や引用符のエスケープなどを考慮しないと、データが破損することがあります。
簡易なデータであれば String.Split で済むこともありますが、実際には以下のような注意点を押さえておく必要があります。
- フィールドを引用符で囲んでいる場合の処理
- 空行や余分な空白のスキップ
- 文字コード/改行コード(CRLF, LF の違いなど)の影響
これらを自前で処理するのは手間がかかり、ミスが生じやすくなります。
ライブラリを使った高度な読み込み: CsvHelper やその他ツールの活用
標準機能の限界を超えて、より複雑な CSV フォーマットや大量データを扱う場合には、専用のライブラリを使うのが実践的です。ここでは最新のライブラリとその使い方、注意点を比較しながら見ていきます。「CsvHelper」などは非常に機能豊富で性能も高く、多くのプロジェクトで採用されています。
CsvHelper の特徴と使い方
CsvHelper は高速かつ柔軟な CSV ライブラリで、クラスマッピング・型変換・区切り文字/引用符の設定など多彩な機能が備わっています。最新バージョンは 33.1.0 で、.NET Standard 2.0 以降および .NET 8 などに対応しており、クロスプラットフォーム開発でも安心です。
基本的な読み込みの例:
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
…
using(var reader = new StreamReader(path, Encoding.UTF8))
using(var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ",", HasHeaderRecord = true }))
{
var records = csv.GetRecords<MyClass>().ToList();
// レコードを List<MyClass> に格納
}
このようにクラスを使って型安全に読み込めます。区切り文字や CultureInfo の設定を変えることで欧米・日本・Excel との互換性を取りやすくなります。
その他の代替ライブラリの比較(TinyCsvParser、Sep、Sylvan.Data.Csv など)
最近のベンチマークでは、CsvHelper は総合力で非常に高く評価される一方、「高速性」や「低メモリ使用」という点で Sep や Sylvan.Data.Csv のようなライブラリが CsvHelper と比べて優れるケースもあります。特にファイルが非常に大きい、またはリアルタイム処理が求められる環境ではこれらの選択肢を検討すべきです。
性能比較表を使って特徴を整理すると以下のようになります。ここでは代表的なライブラリを比較しています。
| ライブラリ | 特徴 | 適した用途 |
|---|---|---|
| CsvHelper | クラスマッピング付き、高機能、柔軟性が高い | 中〜大規模、複雑なフォーマット |
| Sep | 非常に高速、低レベル処理/バイト単位で効率化 | 超大規模データやバッチ処理 |
| TinyCsvParser | 並列処理対応、高速だが機能は限定的 | パフォーマンス重視で機能がシンプルな用途 |
文字コード・改行・Excel互換性など注意すべき細部
CSV処理で見落とされがちな要素に文字コード、改行コード、BOM の取り扱い、Excelでの解釈があります。Excel で開かれることが想定されている CSV では UTF-8 に BOM を付与するか、Excel が期待する形式を意図的に狙う必要があります。また .NET のコードページプロバイダーを登録することで Shift_JIS など日本特有の文字コードにも対応可能です。
また、空行や区切り文字がデータ内部にある場合、引用符で囲っているかどうかを正しく扱わないと行がずれたりデータが壊れたりします。さらに、レコードの歪み(列数が異なる行)が含まれるファイルもあり、読み込み中に例外をスローするかデフォルト値で埋めるかを設計段階で決めておくと混乱を防げます。
実践コード例: 配列とリストに格納する具体的なサンプル
ここでは「CSVの先頭行をヘッダーとし、データ行をクラスのリストに格納する」「配列としてCSV全行を読み込む」など具体的なコード例を通して実践テクニックを共有します。手を動かすことで理解が深まります。
クラス定義を使って List<T> に格納する例(CsvHelper 利用)
以下は「Person」というクラスを定義し、CSV のヘッダーとマッピングして読み込む例です。型安全にデータ操作できます。
public class Person { public string Name { get; set; } public int Age { get; set; } public string Country { get; set; } }
…
using(var reader = new StreamReader(path, Encoding.UTF8))
using(var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true }))
{
var records = csv.GetRecords<Person>().ToList();
// records は List<Person> 型
}
この方法の利点は列名変更や順序入れ替えにも柔軟に対応できることです。
全行を string[][] の配列で格納する例(TextFieldParser 利用)
先頭行も含め、全ての行の全フィールドを配列の配列で持ちたい場合の例です。
List<string[]> rows = new List<string[]>();
using(var parser = new TextFieldParser(path, Encoding.UTF8))
{
parser.Delimiters = new string[] { "," };
while(!parser.EndOfData)
{
string[] fields = parser.ReadFields();
rows.Add(fields);
}
}
string[][] array = rows.ToArray();
このスタイルは行数と列数が確定していないファイルに対しても柔軟に対応できます。
大容量ファイル/非同期読み込みの実装例
ファイルサイズが大きい場合や UI をブロックしたくない場合、非同期 I/O やストリーミング処理が効果的です。CsvHelper は GetRecords<T> を列挙可能な IEnumerable<T> として返すため、全行を一度にメモリに展開せずに逐次処理できます。
例:
using(var reader = new StreamReader(path, Encoding.UTF8))
using(var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture)))
{
await foreach(var record in csv.GetRecordsAsync<MyClass>())
{
// record を処理、List に追加するかストリーム処理
}
}
このように非同期 API を活用すると、メモリ消費を抑えつつスケーラブルな処理が可能です。
よくあるエラーとその対策: トラブルを未然に防ぐポイント
CSV読み込みをする際に開発者が陥りやすい落とし穴とその対処方法を整理します。こうした知識があると、予期しない文字化けや行ずれ、列数不一致などにすぐ対応できるようになります。
文字コード/BOM の誤認と mojibake の発生
CSV ファイルの文字コードがファイルを作成した環境によって変わるため、読み込む側で文字化けを防ぐ設定が重要です。Excel を想定すると UTF-8 に BOM を付けると安全な互換性があります。BOM を含むファイルを UTF-8 指定で読み取るか、CodePagesEncodingProvider を登録して Shift_JIS 等も正しく読み込めるようにすることが推奨されます。
列数や区切り文字の揺らぎによる行ずれ
CSV ファイルには様々な区切り文字(カンマ・セミコロン・タブ)が使われたり、データ中に区切り文字が含まれていたりするため、区切り文字の誤指定や引用符処理漏れで列数が異なる行が発生します。ライブラリを使う場合は区切り文字の設定と HasFieldsEnclosedInQuotes のようなオプションを確認してください。標準機能の場合は自前で検証ロジックを組む必要があります。
数値データの先頭ゼロ消失や日付変換の誤り
電話番号や郵便番号のように先頭ゼロが重要なデータがある場合、文字列型で読み込むことが必要です。さらに Excel が自動で日付と認識してしまうフィールドがあると意図しない変換がされることもあります。CsvHelper やマッピング機能を使い、データ型を明示的に指定することで回避可能です。
性能・可読性・保守性で最適な選択肢を比較する
ある程度規模のあるプロジェクトや将来的な保守を考えると、読み込み性能だけでなく可読性・拡張性・エラー処理の整備も重要です。ここでは複数の観点から選定基準を整理し、どの方法をいつ使うか目安を示します。
性能重視 vs 可読性重視のトレードオフ
非常に大量の CSV を高速に処理したい場合、Sep や Sylvan.Data.Csv 等の低レベルかつ高速なライブラリが適しています。このようなライブラリは文字列の生成を最小限に抑える設計がされており、I/O ストリーミングも支持するものがあります。一方、可読性や保守性を重視する場合は CsvHelper によるクラスマッピングと明示的な設定の方が理解しやすく、後々の機能追加が楽になります。
小規模プロジェクトでの手軽な選び方
プロジェクトが小さい場合やデータフォーマットが非常に単純な場合は、標準機能(StreamReader/String.Split)で十分なことが多いです。ただし将来フォーマットが複雑になる可能性があるなら、最初から CsvHelper を使って設計しておくのが安心です。
保守性とエラー対応の観点
CSV読み込みで想定外の行が入ったり列がずれたりすることはよくあります。例外処理を整えたり、行単位でデータ検証するコードを入れること、単体テストやサンプルデータを複数用意することなどが重要です。ライブラリを利用するなら、エラー発生時の動き(例外スロー/スキップ等)を設定できるものを選びましょう。
使い分けの指針: 標準?ライブラリ?どちらがいいか判断基準
どの方法を採用すべきかを決める判断基準を整理します。要件・環境・データ量などを考えて最適な設計を選ぶことで後悔しない実装になります。
要件分析のチェックリスト
以下の項目を検討し、それぞれに対応可能な読み込み手法を洗い出してください。
- ファイルサイズ(データ件数・ MB 単位か GB 単位か)
- フォーマットの複雑さ(区切り文字・引用符・改行など)
- 文字コード(UTF-8/Shift_JIS/その他)および Excel との互換性
- 実行環境 (.NET のバージョン/Windows/クロスプラットフォーム)
- リアルタイム性やメモリ制約の有無
- 読み込み後に型安全に扱いたいかどうか(クラスマッピング)
おすすめのパターン例
要件に応じて以下のパターンがよく使われます。
- 小さなツールやスクリプト/単純な CSV:StreamReader+String.Split
- Windows 環境で引用符など仕様に準拠したい:TextFieldParser
- 業務用途で型安全・拡張性が必要:CsvHelper を使いクラスマッピングする
- 大量データ/バッチ処理で速度とメモリ効率重視:Sep や Sylvan.Data.Csv などの高速ライブラリ
まとめ
CSV 読み込み処理を C# で実装する際、最も大切なことは「データ形式・文字コード・フォーマットの揺らぎ」を正しく把握し、それに見合った手法を選ぶことです。単純なデータなら標準機能で十分ですが、複雑化するほどライブラリの力が活きます。
CsvHelper は最新の環境にも対応しており、機能・拡張性・互換性のバランスが非常に良いため、多くの開発現場で中心的選択肢となっています。性能が最優先な場面では専用ライブラリを検討し、小規模なら標準機能で素早く実装するのが実践的です。
読み込み後は必ずサンプルデータでテストし、想定外の CSV を使って動作を検証する習慣をつけることで、後からのバグやメンテナンスコストを大きく削減できます。
コメント