[C#][.NET] Shos.CsvHelper (CSV を読み書きするためのシンプルなライブラリー)
csv (Comma-Separated Values または Character-Separated Values) を読み書きするためのシンプルなライブラリーを書いてみた。
csv 形式のファイルは Excel などで表示/編集することができ、便利なので、そこそこ必要になる。
csv を読み書きするライブラリーは既に他にあるが、よりシンプルな使い勝手を目指してみた。
このライブラリーを使うと、プレーンなオブジェクトのコレクション (IEnumerable<Something>) を csv 形式で読み書きすることができる。
使い方
使い方の概要
まず、何か IEnumerable<TElement> なものを用意する:
// 何か IEnumerable<TElement> なもの IEnumerable<ToDo> toDoes = new ToDoList();
例えばこんなクラス:
class ToDoList : IEnumerable<ToDo> // サンプル コレクション { public IEnumerator<ToDo> GetEnumerator() { yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) }; yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High }; yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true }; yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
これを、次のように書きこめる:
const string csvFileName = "todo.csv"; await toDoes.WriteCsvAsync(csvFileName);
出来上がった csv ファイルは次のようになる:
- 値がカンマや区切り文字、改行(<CR>、<LF>)、ダブルクォーテーションを含んでいる場合は、ダブルクォーテーションで囲まれる
- 値の中のダブルクォーテーションは、ダブルクォーテーション2つに置き換わる
Id,Title,Deadline,Done,Priority,Details,DaySpan 1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0 2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3 3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0 4, wish list ,2017/07/12 13:13:01,False,High," (1) ""milk"" (2) shampoo (3) tissue ",0

読みこみは次のような感じ:
IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);
読み書きするもの
コレクションの要素の “get” と “set” の両方を持つ public なプロパティが csv ファイルに読み書きされる。
書きこみ時
書きこみ時には、型を問わず、”ToString()” メソッドで文字列に変換される。
読みこみ時
読みこみ時には、文字列型はそのまま、enum (列挙型) も文字列の通りの値として読みこまれる。
それ以外の型のときは、”TryParse” か “Parse” を使って文字列を値に変更しようとする。
このいずれもできない型は読みこまれない。
“get” と “set” の両方を持っていて、かつ、次のいずれかの型であるプロパティが読みこまれる。
- 文字列型
- enum
- デフォルト コンストラクターを持ち “TryParse” か “Parse” ができる型
(int などの基本的な数値型や DateTime、”TryParse” か “Parse”
を持つユーザー定義型)
その他のルール
- [CsvIgnore()] 属性が付けられたプロパティは、読み書きされない
- [ColumnName(“列名”)] 属性が付けられたプロパティは、csv ファイルでの列名が指定されたものに変更される
例えば、上記 ToDo クラスはこんな型:
// 要素の型のサンプル // 〇 の付いたプロパティ: 読み書きされる // × の付いたプロパティ: 読み書きされない class ToDo { public int Id { get; set; } // 〇 public string Title { get; set; } = ""; // 〇 public DateTime Deadline { get; set; } = DateTime.Now; // 〇 public bool Done { get; set; } // 〇 public Priority Priority { get; set; } = Priority.Middle; // 〇 ユーザー定義 enum [ColumnName("Details")] public string Detail { get; set; } = ""; // 〇 [ColumnName("Details")] で csv ファイルでの列名が "Details" に変更される public DaySpan DaySpan { get; set; } // 〇 "Parse" を持つユーザー定義型 ("TryParse" は持たない) [CsvIgnore()] public string Option { get; set; } = ""; // × [CsvIgnore()] が付けられているので無視される public string Version => "1.0"; // × get しかできないプロパティなので無視される }
上記 ToDo クラスで使われているユーザー定義型は次の通り:
// ユーザー定義の enum の例 enum Priority { High, Middle, Low } // "Parse" を持つユーザー定義型 ("TryParse" は持たない) の例 struct DaySpan { public int Value { get; private set; } public DaySpan(int value) => Value = value; public static DaySpan Parse(string text) => new DaySpan(int.Parse(text)); public override string ToString() => Value.ToString(); }
ヘッダーの有無
csv の読み書きは、ヘッダー部分 (例えば、上記 csv ファイルの一行目) がない場合でも可能になっている。
ただし、ヘッダー部分がある場合は、列が入れ替わっていてもヘッダー部を照合することで読みこみ可能だが、ヘッダー部分がない場合は、列を入れ替えると読みこむことができない。
ヘッダー部分無しでの書きこみの例:
await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);
出来上がった csv ファイル:
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0 2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3 3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0 4, wish list ,2017/07/12 13:13:01,False,High," (1) ""milk"" (2) shampoo (3) tissue ",0

こうしたヘッダー部分の無い csv ファイルを読みこむときは次のようにする:
IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);
その他の指定方法
文字コードは変更可能 (デフォルトは UTF8)。
CsvSerializer.Encoding = Encoding.GetEncoding(0);
区切り文字も変更可能 (デフォルトは ‘,’)。
CsvSerializer.Separator = '\t';
ファイル名の代わりに stream も指定できる。
using (var stream = new FileStream(csvFileName, FileMode.Create)) await collection.WriteCsvAsync(stream);
leaveOpen を指定することで、読み書きの後に stream を開いたままにすることもできる。
using (var stream = new FileStream(csvFileName, FileMode.Create)) await collection.WriteCsvAsync(stream: stream, bufferSize: 1024, leaveOpen:true, hasHeader: true);
非同期メソッド以外に同期メソッドもある。
toDoes.WriteCsv(csvFileName);
NuGet と GitHub
各ライブラリーは NuGet に公開されているので、Visual Studio からインストールできる。
ソースコードは次の場所で公開:
含まれるプロジェクトは次の通り:
Shos.CsvHelper
- Csv ライブラリー
- .NET Standard ライブラリー版
- .NET Standard 1.3 以降でビルドできる
- .NET Network 4.6 以降、または .NET Core 1.1 以降対象
- NuGet パッケージとしてインストールできる: NuGet Gallery | Shos.CsvHelper
Shos.CsvHelper.NetFramework
- Csv ライブラリー
- .NET Framework 版
- .NET Framework 4.5.2 以降でビルドできる
- NuGet パッケージとしてインストールできる: NuGet Gallery | Shos.CsvHelper.NetFramework
Shos.CsvHelperSample.NetCore
- Shos.CsvHelper を利用する .NET Core コンソール アプリケーションのサンプル
Shos.CsvHelperSample.NetFramework
- Shos.CsvHelper.NetFramework を利用する .NET Framework コンソール アプリケーションのサンプル
ディスカッション
コメント一覧
まだ、コメントがありません