【CSV読み込み】TextFieldParserではなく、CsvParserを使用する
2020/02/19追記
CsvParserを使用せずとも、CsvReaderで可能でした。
CSVの解説と各プログラミング言語での実装例
CSVパース時の懸念点
CSVをパースする際、単純に1行ずつ読み込んで、カンマ「,」でSplitするだけで事足りるようなデータであればいいのですが、実際はそうともいかないので、以下のような考慮が必要になるかと思います。
※前提 区切り文字:カンマ「,」
囲み文字:ダブルクオーテーション「"」
各行の列数は同じ
- データ中にカンマが存在する(区切り文字に指定した文字がある)
"a","b","c,d,e"
- データ中にダブルクォーテーションが存在する(ダブルクォーテーションを二重化してエスケープされている)
"a","""b","c"
- データ中に改行が存在する
"a","b
ccc","d"
- 囲んだパターンと囲まないパターンが混在
"a",b,"c,d,e"
他には、データ中にタブ「\t」が存在する等・・・
まずTextFieldParserを使用してみましたが・・・
こういったデータを考慮しながら自前でパースしていくのはなかなか大変なので、というか私は考えることすらやめたので・・・
(いつか挑戦したい)
便利なクラスがないか昔ネットで探したところ、TextFieldParserが良さげのようでした。
TextFiledParser
.Netのクラスなので、他のオープンソースのパーサーと比べて、動作に信頼が持て、コンプライアンス的にみても良いと思っていました。
実際に上記データを読み込んでみると綺麗にパースできます。
使用するCSV
"ヘッダー1","ヘッダー2","ヘッダー3"
"a","b","c,d,e"
"a","""b","c"
"a","b
ccc","d"
"a",b,"c,d,e"
コード
using Microsoft.VisualBasic.FileIO;
using System;
using System.Linq;
using System.Text;
namespace TestProject.CSVParseTest
{
public class CSVParseTest
{
public void ReadCsv()
{
using (var parser = new TextFieldParser(@".\test.csv", Encoding.UTF8))
{
// 区切り文字
parser.Delimiters = new string[] { "," };
// 囲み文字あり
parser.HasFieldsEnclosedInQuotes = true;
while (!parser.EndOfData)
{
var values = parser.ReadFields();
values.ToList().ForEach(v =>
{
// わかりやすいようにパイプを入れる
Console.Write(v + "|");
});
// わかりやすいように改行
Console.WriteLine();
}
}
}
}
}
using System;
namespace TestProject.CSVParseTest
{
public class Program
{
public static void Main(string[] args)
{
try
{
var pTest = new CSVParseTest();
pTest.ReadCsv();
}
catch(Exception err)
{
Console.WriteLine(err.Message);
}
finally
{
Console.Read();
}
}
}
}
上記の結果に満足していたのですが、次のようなデータの場合に、意図しない動きとなってしまいました。
使用するCSV ※データ中に空の改行が入る
"ヘッダー1","ヘッダー2","ヘッダー3"
"a","b","c,d,e"
"a","""b","c"
"a","b
gggg
ffff
ccc","d"
"a",b,"c,d,e"
結果
データ中であっても、空行が削除されてしまうようです。
掲示板などの備考欄等のテキストには、よく空行を入れてみた目を整えることがあるかとおもいます。
そういったデータを扱えなくなるのは困るので不採用としました。
CsvHelperのCsvParserクラスを使用する
他をあたることになり、人気のCsvHelperを検討していました。
CsvHelper
ただ、これを利用する場合、CSVのフォーマットに沿ったエンティティのクラスを用意する必要があり、
CSVの列数が後から変更になったりすると、都度プログラムソースも修正しなければならないと思ったので躊躇していました。
TextFiledParserのように1行分の文字を区切り文字でsplitして配列で返してくれたらなぁ・・・
かつ、空行も再現してくれたらなぁ・・・
なんて思っていたら、CsvHelper.CsvParserのReadメソッドがそれでした。
using CsvHelper;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace TestProject.CSVParseTest
{
public class CSVParseTest2
{
public void ReadCsv()
{
using(var stream = new StreamReader(@".\test.csv", Encoding.UTF8))
using (var parser = new CsvParser(stream, CultureInfo.InvariantCulture))
{
string[] line;
while ((line = parser.Read()) != null)
{
line.ToList().ForEach(v =>
{
// わかりやすいようにパイプを入れる
Console.Write(v + "|");
});
// わかりやすいように改行
Console.WriteLine();
}
}
}
}
}
「TextFieldParser + 空行が削除されない」が実現できました。
おわりに
CsvHelperの中はおそらく1文字ずつ処理して、パースしているんだと思います。
いつか中身を見て勉強してみたいです。
誤認や、他の方法で解決できる等、ありましたら指摘いただけるとありがたいです。
Author And Source
この問題について(【CSV読み込み】TextFieldParserではなく、CsvParserを使用する), 我々は、より多くの情報をここで見つけました https://qiita.com/GodPhwng/items/603814493d853a5f1ff6著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .