web-dev-qa-db-ja.com

C#を使用したCSVファイルの読み取り

単純なインポートアプリケーションを作成しており、CSVファイルを読み取り、DataGridに結果を表示し、CSVファイルの破損した行を別のグリッドに表示する必要があります。たとえば、別のグリッドに5つの値よりも短い線を表示します。私はこのようにしようとしています:

StreamReader sr = new StreamReader(FilePath);
importingData = new Account();
string line;
string[] row = new string [5];
while ((line = sr.ReadLine()) != null)
{
    row = line.Split(',');

    importingData.Add(new Transaction
    {
        Date = DateTime.Parse(row[0]),
        Reference = row[1],
        Description = row[2],
        Amount = decimal.Parse(row[3]),
        Category = (Category)Enum.Parse(typeof(Category), row[4])
    });
}

ただし、この場合、配列を操作することは非常に困難です。値を分割するより良い方法はありますか?

148
ilkin

車輪を再発明しないでください。すでに.NET BCLにあるものを活用してください。

  • Microsoft.VisualBasicへの参照を追加します(はい、VisualBasicと表示されますが、C#でも同様に動作します-最後はすべてILであることに注意してください)
  • Microsoft.VisualBasic.FileIO.TextFieldParserクラスを使用してCSVファイルを解析します

サンプルコードは次のとおりです。

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData) 
    {
        //Processing row
        string[] fields = parser.ReadFields();
        foreach (string field in fields) 
        {
            //TODO: Process field
        }
    }
}

私のC#プロジェクトではとても便利です。

他のリンク/情報は次のとおりです。

335
David Pokluda

私の経験では、多くの異なるcsv形式があります。特に、フィールド内の引用符と区切り文字のエスケープ処理方法。

これらは私が遭遇した亜種です:

  • 引用符は二重引用符で囲まれます(Excel)。つまり、15 "-> field1、" 15 "" "、field3
  • フィールドが他の何らかの理由で引用されない限り、引用は変更されません。つまり、15 "-> field1,15"、fields3
  • 引用符は\でエスケープされます。つまり、15 "-> field1、" 15\""、field3
  • 引用符はまったく変更されません(常に正しく解析できるとは限りません)
  • 区切り文字が引用されます(Excel)。すなわち、a、b-> field1、 "a、b"、field3
  • 区切り文字は\でエスケープされます。すなわち、a、b-> field1、a \、b、field3

私は既存のcsvパーサーの多くを試しましたが、私が遭遇したバリアントを処理できる単一のパーサーはありません。また、パーサーがサポートするバリアントをエスケープするドキュメントから見つけることも困難です。

私のプロジェクトでは、VB TextFieldParserまたはカスタムスプリッターを使用しています。

31
adrianm

NugetのCsvHelper をお勧めします。

(Microsoft.VisualBasicへの参照を追加すると、気分が悪くなります。見苦しいだけでなく、おそらくクロスプラットフォームでもありません。)

20
knocte

車輪の再発明をしたくない場合、ライブラリを使用するのがクールな場合もありますが、この場合、ライブラリを使用する場合と比較して、より少ないコード行で読みやすい同じジョブを実行できます。ここに私が非常に使いやすいと思う別のアプローチがあります。

  1. この例では、StreamReaderを使用してファイルを読み取ります
  2. 各行から区切り文字を検出する正規表現。
  3. インデックス0からnまでの列を収集する配列

using (StreamReader reader = new StreamReader(fileName))
    {
        string line; 

        while ((line = reader.ReadLine()) != null)
        {
            //Define pattern
            Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");

            //Separating columns to array
            string[] X = CSVParser.Split(line);

            /* Do something with X */
        }
    }
11
Mana

CSVは複雑になりますreal高速。

堅牢で十分にテストされたものを使用します。
FileHelpers: www.filehelpers.net

FileHelpersは、ファイル、文字列、またはストリーム内の固定長または区切りレコードからデータをインポート/エクスポートするための無料で使いやすい.NETライブラリです。
6
Keith Blows

私はこれをここで使用します:

http://www.codeproject.com/KB/database/GenericParser.aspx

前回このようなものを探していたとき、これに対する答えとしてそれを見つけました question

3
Stefan Egli

このリストのもう1つ、 Cinchoo ETL -CSVファイルを読み書きするためのオープンソースライブラリ

以下のサンプルCSVファイルの場合

Id, Name
1, Tom
2, Mark

以下のようにライブラリを使用してすばやく読み込むことができます

using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader())
{
   foreach (dynamic item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

CSVファイルに一致するPOCOクラスがある場合

public class Employee
{
   public int Id { get; set; }
   public string Name { get; set; }
}

以下のように使用してCSVファイルをロードできます

using (var reader = new ChoCSVReader<Employee>("test.csv").WithFirstLineHeader())
{
   foreach (var item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

CodeProject の記事をご覧ください。

免責事項:私はこのライブラリの著者です

3
RajN
private static DataTable ConvertCSVtoDataTable(string strFilePath)
        {
            DataTable dt = new DataTable();
            using (StreamReader sr = new StreamReader(strFilePath))
            {
                string[] headers = sr.ReadLine().Split(',');
                foreach (string header in headers)
                {
                    dt.Columns.Add(header);
                }
                while (!sr.EndOfStream)
                {
                    string[] rows = sr.ReadLine().Split(',');
                    DataRow dr = dt.NewRow();
                    for (int i = 0; i < headers.Length; i++)
                    {
                        dr[i] = rows[i];
                    }
                    dt.Rows.Add(dr);
                }

            }

            return dt;
        }

        private static void WriteToDb(DataTable dt)
        {
            string connectionString =
                "Data Source=localhost;" +
                "Initial Catalog=Northwind;" +
                "Integrated Security=SSPI;";

            using (SqlConnection con = new SqlConnection(connectionString))
                {
                    using (SqlCommand cmd = new SqlCommand("spInsertTest", con))
                    {
                        cmd.CommandType = CommandType.StoredProcedure;

                        cmd.Parameters.Add("@policyID", SqlDbType.Int).Value = 12;
                        cmd.Parameters.Add("@statecode", SqlDbType.VarChar).Value = "blagh2";
                        cmd.Parameters.Add("@county", SqlDbType.VarChar).Value = "blagh3";

                        con.Open();
                        cmd.ExecuteNonQuery();
                    }
                }

         }

前の回答を完了するには、CSVファイルのオブジェクトのコレクションが必要になる場合があります。CSVファイルは、TextFieldParserメソッドまたはstring.Splitメソッドで解析され、Reflectionを介して各行がオブジェクトに変換されます。明らかに、最初にCSVファイルの行に一致するクラスを定義する必要があります。

ここにあるMichael KropatのシンプルなCSV Serializerを使用しました: Generic class to CSV(すべてのプロパティ) そして彼のメソッドを再利用して、希望するクラスのフィールドとプロパティを取得しました。

次の方法でCSVファイルをデシリアライズします。

public static IEnumerable<T> ReadCsvFileTextFieldParser<T>(string fileFullPath, string delimiter = ";") where T : new()
{
    if (!File.Exists(fileFullPath))
    {
        return null;
    }

    var list = new List<T>();
    var csvFields = GetAllFieldOfClass<T>();
    var fieldDict = new Dictionary<int, MemberInfo>();

    using (TextFieldParser parser = new TextFieldParser(fileFullPath))
    {
        parser.SetDelimiters(delimiter);

        bool headerParsed = false;

        while (!parser.EndOfData)
        {
            //Processing row
            string[] rowFields = parser.ReadFields();
            if (!headerParsed)
            {
                for (int i = 0; i < rowFields.Length; i++)
                {
                    // First row shall be the header!
                    var csvField = csvFields.Where(f => f.Name == rowFields[i]).FirstOrDefault();
                    if (csvField != null)
                    {
                        fieldDict.Add(i, csvField);
                    }
                }
                headerParsed = true;
            }
            else
            {
                T newObj = new T();
                for (int i = 0; i < rowFields.Length; i++)
                {
                    var csvFied = fieldDict[i];
                    var record = rowFields[i];

                    if (csvFied is FieldInfo)
                    {
                        ((FieldInfo)csvFied).SetValue(newObj, record);
                    }
                    else if (csvFied is PropertyInfo)
                    {
                        var pi = (PropertyInfo)csvFied;
                        pi.SetValue(newObj, Convert.ChangeType(record, pi.PropertyType), null);
                    }
                    else
                    {
                        throw new Exception("Unhandled case.");
                    }
                }
                if (newObj != null)
                {
                    list.Add(newObj);
                }
            }
        }
    }
    return list;
}

public static IEnumerable<MemberInfo> GetAllFieldOfClass<T>()
{
    return
        from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
        where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
        let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
        orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
        select mi;            
}
0
EricBDev

まず、CSVとは何か、それをどのように記述するかを理解する必要があります。

  1. すべての次の文字列(/r/n)は次の「テーブル」行です。
  2. 「表」セルは、区切り記号で区切られています。最も頻繁に使用されるシンボルは\tまたは,です
  3. すべてのセルにこの区切り記号を含めることができます(この場合、セルは引用符で始まり、この記号で終わる必要があります)
  4. すべてのセルに/r/n記号を含めることができます(この場合、セルは引用符で始まり、この記号で終わる必要があります)

C#/ Visual BasicがCSVファイルを操作する最も簡単な方法は、標準のMicrosoft.VisualBasicライブラリを使用することです。必要な参照と次の文字列をクラスに追加するだけです。

using Microsoft.VisualBasic.FileIO;

はい、C#で使用できます。心配しないでください。このライブラリは比較的大きなファイルを読み取ることができ、必要なすべてのルールをサポートしているため、すべてのCSVファイルを操作できます。

少し前に、このライブラリに基づいてCSV読み取り/書き込み用の簡単なクラスを作成しました。この単純なクラスを使用すると、2次元配列のようにCSVを操作できます。次のリンクでクラスを見つけることができます: https://github.com/ukushu/DataExporter

使用の簡単な例:

Csv csv = new Csv("\t");//delimiter symbol

csv.FileOpen("c:\\file1.csv");

var row1Cell6Value = csv.Rows[0][5];

csv.AddRow("asdf","asdffffff","5")

csv.FileSave("c:\\file2.csv");
0
Andrew