C#でプログラムを開発していると、アプリケーションで CSV ファイルを読み込みしなければならない場面がたまにあります。
単純に「カンマ(,)」で分割すればいいと思うかもしれませんが、CSVファイルの仕様て扱う現場によって結構バラバラだったりします。
こうなると単純に分割しただけではうまくいきません。
そこでオススメするライブラリが「CsvHelper」です。CsvHelper は簡単にCSV ファイルの読み書きが行えるライブラリで、柔軟に csv ファイルを扱うことが可能です。
この記事では、2023年9月時点で最新バージョンである「CsvHelper」を利用して CSV ファイルを読み込む方法について紹介しています。
オススメの参考書
C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。
CSVの仕様
CSV とは Comma Separated Values の略でカンマ区切りで並べた値という意味で、その CSV の形式で保存されたファイルを CSV ファイル(拡張子:*.csv)と言います。
CSV ファイルはデータ交換用フォーマットとして最も普及したデータ交換用のフォーマットです。
広く利用されている割に CSV は明確な決まり事がなく、扱う場面によって仕様が異なる事が多々あります。
例えば、ファイルの先頭にはヘッダーとなる行があってもなくてもいいですし、ファイル末尾のレコードの終端には改行があってもなくてもいいのです。
他にも次のような項目で仕様が異なることがあります。
- ヘッダー行
ファイルの先頭にはヘッダー行があってもなくてもいい - ファイル末尾
ファイル末尾の行の終端には改行があってもなくてもいい - CSVの値
文字列は「”(ダブルクォーテーション)」で囲んでも囲まなくてもいい
※値の中に「,(カンマ)」や「改行<CR><LF>」など制御文字がある場合は「”」で囲む必要がある。 - 区切り文字
基本は「,(カンマ)」で区切るが、「タブ<TAB>」で区切ってもいい - 改行コード
各行の改行は<CR><LF>とする。
※実行環境によって異なり、UNIXの場合は<LF>
C# で CSV ファイルを読み取りする方法として、「,(カンマ)」を Split メソッドで分割して string 配列にする方法があります。
この方法は簡単に読み取りができる一方で、データの中にカンマがあるとそこで分割されてしまいます。この結果、読み取りしたデータが想定した通りに string 配列に入っていない場合があり、変換前にチェックする関数など用意してあげる必要があります。
こういう面倒なことをしたくない場合は「CsvHelper」というライブラリを利用するのがオススメです。さまざまな機能を有している為、仕様の違いに対して柔軟にCSVファイルの読み取りを行う事ができます。
では CsvHelper について詳しくみてみましょう。
CsvHelperとは
CsvHelper は、CSV ファイルの読み込みと書き込みを行う為のオープンソースの .NET ライブラリです。非常に高速かつ柔軟に CSV ファイル 扱うことができます。
このライブラリは次のような特徴が挙げられます。
CsvHelperの特徴
- 商用利用可能なオープンソースライブラリである
- 柔軟にCSVファイルの読み取りができる
- CSVファイルをクラスへマッピングする機能がある
商用利用可能なオープンソースライブラリである
CsvHelper は NuGet からインストールする事が可能です。もちろん、前述している通り無料で利用ができます。
インストール手順は次の通りです。
Visual Studio の「ツール」 -> 「NuGet パッケージ マネージャー」 -> 「ソリューションの NuGet パッケージの管理」を選択します。
「参照」タブを選択して、検索欄に「CsvHelper」を入力します。検索結果に表示された「CsvHelper」を選択します。
チェックボックスにチェックを入れて、「インストール」ボタンをクリックします。
ここでは、2023年9月時点で最新の安定版である 「30.0.1」 をインストールしています。
柔軟にCSVファイルの読み取りができる
CsvConfiguration のプロパティをいじくる事で様々な仕様に合わせた形で CSV ファイルを読み取りすることができます。ここではよく使うプロパティについて紹介をしています。
ヘッダー行の選択
ファイルの先頭にはヘッダー行が有る場合と無い場合があります。
ヘッダー有り
ヘッダー無し
Configuration の HasHeaderRecord プロパティで true または false を選択することで、ヘッダー行の読み込み設定ができます。
//ヘッダー有り
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true
};
//ヘッダー無し
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};
区切りの選択
CSVの区切り文字は基本的に「,(カンマ)」ですが、それ以外の制御文字が使われている場合があります。例えば、「スペース」や「セミコロン」がカンマの代わりとして使われる場合があります
区切り文字:カンマ
区切り文字:スペース
Configuration の Delimiter プロパティで区切り文字を選択することができます。
//カンマ区切り
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ","
};
//スペース区切り
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "U+002C"
};
改行コードの選択
ファイルの改行コードが実行環境によって異なる場合があります。改行コードには、「\r\n」「\r」「\n」があります。
Configuration の NewLine プロパティで適切な改行コードに設定することで正確にCSVの読み込みを行うことができます。
//改行コード \r\n
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
NewLine = "\r\n",
};
//実行環境で定義されている改行コード
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
NewLine = Environment.NewLine,
};
空行の無視の有無
CSVファイルに空白行があった場合に読み取りをするかしないかを設定できます。
Configuration の IgnoreBlankLines プロパティが true の場合は無視をして次の行を読み取りし、falseの場合は読み取りをします。
//空白を無視する
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
IgnoreBlankLines = true,
};
//空白を無視しない
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
IgnoreBlankLines = false,
};
エンコードの選択
エンコードとは簡単に言うと、ある規則に基づいてデータを変換することです。CSV ファイルもこのエンコードに基づいてデータが保存されています。
CSV を読み取りする際、エンコードが異なると文字化けが発生する可能性があります。
Configuration の Encoding プロパティで適切なエンコードに設定しておきましょう。
// UTF-8
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Encoding = Encoding.UTF8,
};
// ASCII(7ビット)
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Encoding = Encoding.ASCII,
};
コメントの有無
CSVファイル内にコメントが記入されていることがあります。CsvHelperではコメントの有無を設定することができます。
Configuration の AllowComments プロパティが true ならコメントを許可し、falseの場合はコメントを拒否します。
また、先頭の文字列にはコメントを意味する文字列が必ずあるので、Comment プロパティでその文字列を設定します。
// コメント許可
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
AllowComments = true,
Comment = '#',
};
// コメント拒否
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
AllowComments = false,
};
列のフィールドの増減チェック
列のフィールドが前後データフィールドが異なる場合があります。もしくは CSV ファイルを編集した時にフィールドを誤って消したり、増やしてしまう場合があります。
列数をチェックするには Configuration の DetectColumnCountChanges プロパティを true にします。列数が異なる行を検出したら例外(CsvHelper.BadDataException)が発生します。
列数のチェックをしない場合は、false にします。
// 列数をチェックする
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
DetectColumnCountChanges = true,
};
// 列数をチェックしない
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
DetectColumnCountChanges = false,
};
フィールドの前後の空白削除
フィールド内に存在する空白を取り除くには、 Configuration の TrimOptions プロパティを設定します。
// 空白を削除
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
TrimOptions = TrimOptions.Trim,
};
CSVファイルをクラスへマッピングする機能
CSVファイルの列をクラスのどのプロパティにセットするかを簡単に管理することができます。
プロパティの位置をインデックスの位置と紐づけをして使用します。
この方法については後述で記載していますので確認してみて下さい。
CsvHelperの使い方
それでは C# で CSV ファイルを読み込む方法をサンプルを交えながら紹介をします。
CsvHelper は更新頻度が高いため、インターネットで検索するサンプルは前のバージョンで動作するものが多く、ライブラリの最新バージョンをダウンロードするとサンプルをコピペしても動かないなんてことがあります。
ここで紹介するサンプルは2023年9月時点で最新の安定版である「30.0.1」を使っています。
CSVのサンプルファイル
ここで使うサンプルは次のようなCSVファイルです。
1行目はヘッダー(項目名)があり、2行目はコメントがあります。
3行目から7行目までフィールドで、フィールド内に空白を含むデータや6行目には空白行が存在します。
no,name,birthDay,bloodType
#ここからフィールドです。
1,"広瀬すず","6月19日","AB"
2 ,福山雅治 ,2月6日 ,O
3,菅田将暉 ,2月21日 ,A
4,長澤まさみ,6月3日,A
CSVの読み取り
CSV のフィールドをプロパティにセットするクラスを作成します。
この時、CSV の1列がクラスの1つのプロパティとマッピングされるようにします。どの列のフィールドをどのプロパティにデータを渡すかを指定するには Attributes(属性)を使います。
属性はインデックスまたはヘッダー名を使用することができます。ただし、ヘッダー名を属性とする場合はCSVファイルの1行目にヘッダーの名前が存在する場合に限ります。
属性:インデックスの場合
using CsvHelper.Configuration.Attributes;
public class ParsonalInfo
{
[Index(0)]
public int No { get; set; } //No
[Index(1)]
public string Name { get; set; } //名前
[Index(2)]
public string BirthDay { get; set; } //誕生日
[Index(3)]
public string BloodType { get; set; } //血液型
}
属性:ヘッダー名の場合
using CsvHelper.Configuration.Attributes;
public class ParsonalInfo
{
[Name("no")]
public int No { get; set; } //No
[Name("name")]
public string Name { get; set; } //名前
[Name("birthDay")]
public string BirthDay { get; set; } //誕生日
[Name("bloodType")]
public string BloodType { get; set; } //血液型
}
CSV ファイルを読み取りするサンプルコードは次の通りです。関数の引数として、CSV のファイル名を渡します。
public void ReadCsvFile(string fileName)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true,
Delimiter = ",",
NewLine = Environment.NewLine,
IgnoreBlankLines = true,
Encoding = Encoding.UTF8,
AllowComments = true,
Comment = '#',
DetectColumnCountChanges = true,
TrimOptions = TrimOptions.Trim,
};
using (var reader = new StreamReader(fileName, Encoding.UTF8))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<ParsonalInfo>();
foreach (var record in records)
{
Debug.WriteLine("----------------------------");
Debug.WriteLine($"No:{record.No}");
Debug.WriteLine($"Name:{record.Name}");
Debug.WriteLine($"Birth Day:{record.BirthDay}");
Debug.WriteLine($"Blood Type:{record.BloodType}");
}
}
}
上記のソースコードを実行した結果は次のようになります。
—————————-
No:1
Name:広瀬すず
Birth Day:6月19日
Blood Type:AB
—————————-
No:2
Name:福山雅治
Birth Day:2月6日
Blood Type:O
—————————-
No:3
Name:菅田将暉
Birth Day:2月21日
Blood Type:A
—————————-
No:4
Name:長澤まさみ
Birth Day:6月3日
Blood Type:A
このように CSV を読み込むことができます。
今回オプションで空白行はスキップ、フィールドの空白の削除を行っています。実行結果からオプションで指定した通りに読み取りができていることが分かります。
まとめ
この記事では CsvHelper を使って CSV ファイルを読み込む方法について紹介しました。
CsvHelper は仕様が曖昧な CSV ファイルでも、Configuration クラスのプロパティを設定することで、柔軟に CSV を読み込めるオススメの無料ライブラリです。
また、CSV ファイルの列をクラスのどのプロパティにセットするかを簡単に管理できるので、是非活用してみてください。
以上、最後まで読んで頂きありがとうございました。