Parsing file CSV dalam C #, dengan header

266

Apakah ada cara default / resmi / disarankan untuk mem-parsing file CSV di C #? Saya tidak ingin menggulung parser saya sendiri.

Juga, saya telah melihat contoh orang yang menggunakan ODBC / OLE DB untuk membaca CSV melalui driver Teks, dan banyak orang tidak menyarankan ini karena "kekurangannya." Apa kekurangannya ini?

Idealnya, saya mencari cara di mana saya bisa membaca CSV dengan nama kolom, menggunakan catatan pertama sebagai nama header / field. Beberapa jawaban yang diberikan sudah benar tetapi pada dasarnya berfungsi untuk membatalkan deserialisasi file ke dalam kelas.

David Pfeffer
sumber

Jawaban:

138

Biarkan perpustakaan menangani semua detail seluk beluk untuk Anda! :-)

Lihatlah FileHelpers dan tetap KERING - Jangan Ulangi Diri Anda Sendiri - tidak perlu menemukan kembali roda dalam waktu sejuta ....

Anda pada dasarnya hanya perlu mendefinisikan bentuk data Anda - bidang di baris individu Anda di CSV - dengan menggunakan kelas publik (dan atribut yang dipikirkan dengan baik seperti nilai default, penggantian untuk nilai NULL dan sebagainya), titik mesin FileHelpers di file, dan bingo - Anda mendapatkan kembali semua entri dari file itu. Satu operasi sederhana - kinerja hebat!

marc_s
sumber
1
sampai Anda memerlukan sth benar-benar kustom (dan sebagian besar yang dapat diimplementasikan sebagai ekstensi pula) FileHelpers sejauh ini adalah cara terbaik untuk pergi, benar-benar nyaman, teruji dan solusi berkinerja baik
mikus
3
Pada 1 Juni 2015, satu-satunya cara saya bisa mengunduh FileHelpers adalah mencarinya di sourceforge.net. Inilah tautan yang digunakan: sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra
2
@dotnetguy kita akan merilis 3.1 (saat ini 3.1-rc2) tidak aktif. Kami juga mendesain ulang situs: www.filehelpers.net Anda dapat mengunduh versi terbaru dari sana
Marcos Meli
1
@MarcosMeli terima kasih banyak! Saya sudah menggunakan FileHelpers di salah satu proyek saya dan mudah digunakan - pujian untuk tim. Saya berencana blog di atasnya segera dan btw - Sukai situs baru - dilakukan dengan baik!
Sudhanshu Mishra
FileHelpers tidak menangani koma yang dikutip dalam CSV dengan benar, atau benar-benar memetakan header bidang, sebagai gantinya mengharapkan bahwa kolom berada dalam urutan yang sama dengan bidang dideklarasikan dalam tipe Anda. Saya tidak akan menggunakannya, secara pribadi.
Alastair Maw
358

Pengurai CSV sekarang menjadi bagian dari .NET Framework.

Tambahkan referensi ke Microsoft.VisualBasic.dll (berfungsi dengan baik dalam C #, jangan pedulikan namanya)

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

Dokumen ada di sini - Kelas TextFieldParser

PS Jika Anda memerlukan eksportir CSV , coba CsvExport (sebutkan: Saya salah satu kontributor)

Alex
sumber
2
Dari pengalaman saya, TextFieldParser tidak berkinerja baik dengan file besar (mis.> 250MB). :(
MBoros
6
TextFieldParser mengimplementasikan IDisposable, jadi mungkin yang terbaik untuk menggunakannya dalam klausa use. Jawaban yang bagus sebaliknya.
Chris Bush
3
Dalam konstruktor Anda mungkin ingin menggunakan pengkodean yang berbeda dari yang secara default, seperti: TextFieldParser baru ("c: \ temp \ test.csv", System.Text.Encoding.UTF8)
neural5torm
1
Perhatikan bahwa jika bidang apa pun di CSV Anda berisi baris kosong, bidang itu akan dilewati TextFieldParser.ReadLine(). Lihat dokumen TextFieldParser
mcNux
3
Apakah ada cara untuk mendapatkannya di .NET Core?
Hugo Zink
183

CsvHelper (perpustakaan yang saya kelola ) akan membaca file CSV menjadi objek khusus.

var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();

Terkadang Anda tidak memiliki objek yang ingin Anda baca. Dalam hal ini, Anda bisa menggunakan pemetaan fasih karena Anda tidak bisa meletakkan atribut di kelas.

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}

EDIT:

CsvReader sekarang membutuhkan CultureInfo untuk diteruskan ke dalam konstuktor ( https://github.com/JoshClose/CsvHelper/issues/1441 ).

Contoh:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
Josh Close
sumber
18
Saya setuju dengan @ kubal5003. Apa yang menjual saya padanya adalah Anda memilikinya tersedia sebagai paket NuGet. Terima kasih kawan, cepat, dan apakah semua csv membaca yang saya butuhkan.
Gromer
7
Cepat sekali. 1,3 juta catatan dibaca dan di-deseralisasi dalam 10 detik.
marisks
2
Perpustakaan hebat sangat mudah diimplementasikan. Saya hanya menyarankan kepada Josh untuk memperbarui jawabannya di sini karena perpustakaan telah sedikit berubah sejak jawaban ini ditulis dan Anda tidak dapat membuat Instansiasi CsvHelper lagi (ini hanya namespace sekarang) tetapi Anda harus menggunakan kelas CsvReader.
Marko
1
CsvClassMap tampaknya tidak ada di versi terakhir CsvHelper?
knocte
1
knocte, itu disebut ClassMap sekarang. Ada perubahan lain juga, seperti harus membaca sebelum meminta catatan header (yang dengan cara akan diatur ke apa pun yang dibaca oleh panggilan pertama untuk Baca ()). Seperti yang telah disebutkan orang lain sebelumnya, ini sangat cepat dan mudah digunakan.
norgie
31

Dalam aplikasi bisnis, saya menggunakan proyek Open Source di codeproject.com , CSVReader .

Ini bekerja dengan baik, dan memiliki kinerja yang baik. Ada beberapa pembandingan pada tautan yang saya berikan.

Contoh sederhana, disalin dari halaman proyek:

using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true))
{
    int fieldCount = csv.FieldCount;
    string[] headers = csv.GetFieldHeaders();

    while (csv.ReadNextRecord())
    {
        for (int i = 0; i < fieldCount; i++)
            Console.Write(string.Format("{0} = {1};", headers[i], csv[i]));

        Console.WriteLine();
    }
}

Seperti yang Anda lihat, ini sangat mudah untuk dikerjakan.

Alex
sumber
12

Jika Anda hanya perlu membaca file csv maka saya merekomendasikan perpustakaan ini: Pembaca CSV Cepat
Jika Anda juga perlu membuat file csv maka gunakan yang ini: FileHelpers

Keduanya gratis dan opensource.

Giorgi
sumber
FileHelpers memiliki ringkasan yang menarik: filehelpers.com FileHelpers adalah perpustakaan .NET gratis untuk mengimpor / mengekspor data dari panjang tetap atau catatan terbatas dalam file, string atau stream.
AnneTheAgile
Meskipun tautan ini dapat menjawab pertanyaan, hanya tautan jawaban yang tidak disarankan di Stack Overflow, Anda dapat meningkatkan jawaban ini dengan mengambil bagian-bagian penting dari tautan dan memasukkannya ke dalam jawaban Anda, ini memastikan jawaban Anda masih merupakan jawaban jika tautan diubah atau dihapus :)
WhatsThePoint
11

Ini adalah kelas pembantu yang sering saya gunakan, kalau-kalau ada yang kembali ke utas ini (saya ingin membagikannya).

Saya menggunakan ini untuk kesederhanaan porting ke proyek yang siap digunakan:

public class CSVHelper : List<string[]>
{
  protected string csv = string.Empty;
  protected string separator = ",";

  public CSVHelper(string csv, string separator = "\",\"")
  {
    this.csv = csv;
    this.separator = separator;

    foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
    {
      string[] values = Regex.Split(line, separator);

      for (int i = 0; i < values.Length; i++)
      {
        //Trim values
        values[i] = values[i].Trim('\"');
      }

      this.Add(values);
    }
  }
}

Dan gunakan seperti:

public List<Person> GetPeople(string csvContent)
{
  List<Person> people = new List<Person>();
  CSVHelper csv = new CSVHelper(csvContent);
  foreach(string[] line in csv)
  {
    Person person = new Person();
    person.Name = line[0];
    person.TelephoneNo = line[1];
    people.Add(person);
  }
  return people;
}

[Pembantu csv yang diperbarui: bug diperbaiki di mana karakter baris baru terakhir membuat baris baru]

Base33
sumber
17
jika salah satu dari entri csv berisi koma (,) kode ini tidak akan berfungsi.
hakan
Agar segala sesuatunya tetap ringan, saya menggunakan karakter pipa sebagai pemisah. '|'
Base33
solusi terbaik Hanya pertanyaan tentang cuplikan ke-2. Apa jenis objeknya adalah Person
Cocoa Dev
@CocoaDev Ini adalah kelas yang berisi dua properti string - Name dan TelephoneNo. Murni untuk contohnya sekalipun. Jika salah satu properti adalah bilangan bulat, itu harus berupa konversi langsung (dengan centang?).
Base33
10

Solusi ini menggunakan perakitan Microsoft.VisualBasic resmi untuk mengurai CSV.

Keuntungan:

  • pembatas melarikan diri
  • abaikan Header
  • memangkas ruang
  • abaikan komentar

Kode:

    using Microsoft.VisualBasic.FileIO;

    public static List<List<string>> ParseCSV (string csv)
    {
        List<List<string>> result = new List<List<string>>();


        // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. 
        using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) 
        {
            parser.CommentTokens = new string[] { "#" };
            parser.SetDelimiters(new string[] { ";" });
            parser.HasFieldsEnclosedInQuotes = true;

            // Skip over header line.
            //parser.ReadLine();

            while (!parser.EndOfData)
            {
                var values = new List<string>();

                var readFields = parser.ReadFields();
                if (readFields != null)
                    values.AddRange(readFields);
                result.Add(values);
            }
        }

        return result;
    }
Jonas_Hess
sumber
7

Saya telah menulis TinyCsvParser untuk .NET, yang merupakan salah satu parser .NET tercepat di sekitar dan sangat dapat dikonfigurasi untuk mem-parsing hampir semua format CSV.

Ini dirilis di bawah Lisensi MIT:

Anda dapat menggunakan NuGet untuk menginstalnya. Jalankan perintah berikut di Package Manager Console .

PM> Install-Package TinyCsvParser

Pemakaian

Bayangkan kita memiliki daftar Orang dalam file CSV persons.csvdengan nama depan, nama belakang, dan tanggal lahir mereka.

FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02

Model domain yang sesuai di sistem kami mungkin terlihat seperti ini.

private class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

Saat menggunakan TinyCsvParser Anda harus menentukan pemetaan antara kolom dalam data CSV dan properti dalam model domain Anda.

private class CsvPersonMapping : CsvMapping<Person>
{

    public CsvPersonMapping()
        : base()
    {
        MapProperty(0, x => x.FirstName);
        MapProperty(1, x => x.LastName);
        MapProperty(2, x => x.BirthDate);
    }
}

Dan kemudian kita bisa menggunakan pemetaan untuk mem-parsing data CSV dengan a CsvParser.

namespace TinyCsvParser.Test
{
    [TestFixture]
    public class TinyCsvParserTest
    {
        [Test]
        public void TinyCsvTest()
        {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
            CsvPersonMapping csvMapper = new CsvPersonMapping();
            CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);

            var result = csvParser
                .ReadFromFile(@"persons.csv", Encoding.ASCII)
                .ToList();

            Assert.AreEqual(2, result.Count);

            Assert.IsTrue(result.All(x => x.IsValid));

            Assert.AreEqual("Philipp", result[0].Result.FirstName);
            Assert.AreEqual("Wagner", result[0].Result.LastName);

            Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
            Assert.AreEqual(5, result[0].Result.BirthDate.Month);
            Assert.AreEqual(12, result[0].Result.BirthDate.Day);

            Assert.AreEqual("Max", result[1].Result.FirstName);
            Assert.AreEqual("Mustermann", result[1].Result.LastName);

            Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
            Assert.AreEqual(1, result[1].Result.BirthDate.Month);
            Assert.AreEqual(1, result[1].Result.BirthDate.Day);
        }
    }
}

Panduan pengguna

Panduan Pengguna lengkap tersedia di:

bytefish
sumber
1

Berikut ini adalah implementasi KISS saya ...

using System;
using System.Collections.Generic;
using System.Text;

class CsvParser
{
    public static List<string> Parse(string line)
    {
        const char escapeChar = '"';
        const char splitChar = ',';
        bool inEscape = false;
        bool priorEscape = false;

        List<string> result = new List<string>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < line.Length; i++)
        {
            char c = line[i];
            switch (c)
            {
                case escapeChar:
                    if (!inEscape)
                        inEscape = true;
                    else
                    {
                        if (!priorEscape)
                        {
                            if (i + 1 < line.Length && line[i + 1] == escapeChar)
                                priorEscape = true;
                            else
                                inEscape = false;
                        }
                        else
                        {
                            sb.Append(c);
                            priorEscape = false;
                        }
                    }
                    break;
                case splitChar:
                    if (inEscape) //if in escape
                        sb.Append(c);
                    else
                    {
                        result.Add(sb.ToString());
                        sb.Length = 0;
                    }
                    break;
                default:
                    sb.Append(c);
                    break;
            }
        }

        if (sb.Length > 0)
            result.Add(sb.ToString());

        return result;
    }

}
Alex Begun
sumber
1
Ini tidak berurusan dengan jeda baris dalam string yang dikutip yang valid dalam file CSV.
John Leidegren
Alex, yang ingin dikatakan John adalah bahwa RFC 4180 ( ietf.org/rfc/rfc4180.txt - Lihat bagian 2 dan item 6) memungkinkan kolom memiliki CR LF di tengah kolom yang secara efektif menyebarkannya ke atas 2 baris dalam sebuah file. Solusi Anda mungkin akan bekerja dengan baik dalam banyak kasus (terutama jika file CSV dibuat dengan menyimpan dari Excel), tetapi itu tidak mencakup kasus tepi ini. CsvHelper, yang disebutkan di atas, seharusnya mempertimbangkan kasus ini.
David Yates
Ya, ini benar, tetapi jika Anda memiliki CR LF di CSV Anda, kemungkinan Anda seharusnya tidak menggunakan CSV, tetapi sesuatu yang lebih tepat seperti, json, atau xml, atau format panjang tetap.
Alex Begun
1

Beberapa waktu yang lalu saya menulis kelas sederhana untuk CSV baca / tulis berdasarkan Microsoft.VisualBasicperpustakaan. Menggunakan kelas sederhana ini Anda akan dapat bekerja dengan CSV seperti dengan array 2 dimensi. Anda dapat menemukan kelas saya dengan tautan berikut: https://github.com/ukushu/DataExporter

Contoh penggunaan sederhana:

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");

Untuk membaca tajuk saja yang Anda butuhkan hanyalah membaca csv.Rows[0]sel :)

Andrew
sumber
1

Solusi file sumber tunggal untuk kebutuhan penguraian langsung, bermanfaat. Berurusan dengan semua kasus tepi yang jahat. Seperti normalisasi baris baru dan penanganan baris baru dalam string literal yang dikutip. Sama-sama!

Jika file CSV Anda memiliki tajuk, Anda cukup membacakan nama kolom (dan menghitung indeks kolom) dari baris pertama. Sederhana seperti itu.

Perhatikan bahwa Dumpini adalah metode LINQPad, Anda mungkin ingin menghapusnya jika Anda tidak menggunakan LINQPad.

void Main()
{
    var file1 = "a,b,c\r\nx,y,z";
    CSV.ParseText(file1).Dump();

    var file2 = "a,\"b\",c\r\nx,\"y,z\"";
    CSV.ParseText(file2).Dump();

    var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
    CSV.ParseText(file3).Dump();

    var file4 = "\"\"\"\"";
    CSV.ParseText(file4).Dump();
}

static class CSV
{
    public struct Record
    {
        public readonly string[] Row;

        public string this[int index] => Row[index];

        public Record(string[] row)
        {
            Row = row;
        }
    }

    public static List<Record> ParseText(string text)
    {
        return Parse(new StringReader(text));
    }

    public static List<Record> ParseFile(string fn)
    {
        using (var reader = File.OpenText(fn))
        {
            return Parse(reader);
        }
    }

    public static List<Record> Parse(TextReader reader)
    {
        var data = new List<Record>();

        var col = new StringBuilder();
        var row = new List<string>();
        for (; ; )
        {
            var ln = reader.ReadLine();
            if (ln == null) break;
            if (Tokenize(ln, col, row))
            {
                data.Add(new Record(row.ToArray()));
                row.Clear();
            }
        }

        return data;
    }

    public static bool Tokenize(string s, StringBuilder col, List<string> row)
    {
        int i = 0;

        if (col.Length > 0)
        {
            col.AppendLine(); // continuation

            if (!TokenizeQuote(s, ref i, col, row))
            {
                return false;
            }
        }

        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == ',')
            {
                row.Add(col.ToString().Trim());
                col.Length = 0;
                i++;
            }
            else if (ch == '"')
            {
                i++;
                if (!TokenizeQuote(s, ref i, col, row))
                {
                    return false;
                }
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }

        if (col.Length > 0)
        {
            row.Add(col.ToString().Trim());
            col.Length = 0;
        }

        return true;
    }

    public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
    {
        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == '"')
            {
                // escape sequence
                if (i + 1 < s.Length && s[i + 1] == '"')
                {
                    col.Append('"');
                    i++;
                    i++;
                    continue;
                }
                i++;
                return true;
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }
        return false;
    }
}
John Leidegren
sumber
1

Satu lagi dalam daftar ini, Cinchoo ETL - pustaka sumber terbuka untuk membaca dan menulis banyak format file (CSV, file flat, Xml, JSON dll)

Contoh di bawah ini menunjukkan cara membaca file CSV dengan cepat (Tidak perlu objek POCO)

string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine($"Id: {rec.Id}");
        Console.WriteLine($"Name: {rec.Name}");
    }
}

Contoh di bawah ini menunjukkan cara membaca file CSV menggunakan objek POCO

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void CSVTest()
{
    string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

    using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
        .WithFirstLineHeader()
        )
    {
        foreach (var rec in p)
        {
            Console.WriteLine($"Id: {rec.Id}");
            Console.WriteLine($"Name: {rec.Name}");
        }
    }
}

Silakan periksa artikel di CodeProject tentang cara menggunakannya.

RajN
sumber
0

Berdasarkan posting unlimit pada Bagaimana cara membagi CSV menggunakan fungsi C # split () dengan benar? :

string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");

CATATAN: ini tidak menangani koma kabur / bersarang, dll., Dan karenanya hanya cocok untuk daftar CSV sederhana tertentu.

radsdau
sumber
2
Ini sangat buruk dan mungkin lambat :)
EKS
1
Mungkin, tetapi berfungsi dengan baik dan sederhana untuk sejumlah kecil parameter, oleh karena itu merupakan solusi yang valid dan bermanfaat. Kenapa downvote itu? "Sangat buruk" sedikit ekstrem, bukan begitu?
radsdau
1
Itu tidak menangani koma kabur / bersarang, dll. Akan bekerja dalam beberapa kasus tetapi pasti tidak akan bekerja untuk semua file csv
NStuke
Anda benar; Saya akan mengedit balasan untuk mencerminkan itu. Terima kasih. Tetapi masih memiliki tempatnya.
radsdau
Ini bekerja dengan baik untuk kasus penggunaan saya di mana saya sedang membangun dll sql server clr dan tidak dapat menggunakan paket eksternal lainnya. Saya hanya perlu mengurai file csv sederhana dengan nama file dan jumlah baris.
dubvfan87
0

Kode ini membaca csv ke DataTable:

public static DataTable ReadCsv(string path)
{
    DataTable result = new DataTable("SomeData");
    using (TextFieldParser parser = new TextFieldParser(path))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        bool isFirstRow = true;
        //IList<string> headers = new List<string>();

        while (!parser.EndOfData)
        {
            string[] fields = parser.ReadFields();
            if (isFirstRow)
            {
                foreach (string field in fields)
                {
                    result.Columns.Add(new DataColumn(field, typeof(string)));
                }
                isFirstRow = false;
            }
            else
            {
                int i = 0;
                DataRow row = result.NewRow();
                foreach (string field in fields)
                {
                    row[i++] = field;
                }
                result.Rows.Add(row);
            }
        }
    }
    return result;
}
polina-c
sumber
1
TextFieldParser ada di Microsoft.VisualBasic.dll.
user3285954
0

Jika ada yang ingin potongan, mereka dapat menceburkan diri ke dalam kode mereka tanpa harus mengikat pustaka atau mengunduh paket. Ini adalah versi yang saya tulis:

    public static string FormatCSV(List<string> parts)
    {
        string result = "";

        foreach (string s in parts)
        {
            if (result.Length > 0)
            {
                result += ",";

                if (s.Length == 0)
                    continue;
            }

            if (s.Length > 0)
            {
                result += "\"" + s.Replace("\"", "\"\"") + "\"";
            }
            else
            {
                // cannot output double quotes since its considered an escape for a quote
                result += ",";
            }
        }

        return result;
    }

    enum CSVMode
    {
        CLOSED = 0,
        OPENED_RAW = 1,
        OPENED_QUOTE = 2
    }

    public static List<string> ParseCSV(string input)
    {
        List<string> results;

        CSVMode mode;

        char[] letters;

        string content;


        mode = CSVMode.CLOSED;

        content = "";
        results = new List<string>();
        letters = input.ToCharArray();

        for (int i = 0; i < letters.Length; i++)
        {
            char letter = letters[i];
            char nextLetter = '\0';

            if (i < letters.Length - 1)
                nextLetter = letters[i + 1];

            // If its a quote character
            if (letter == '"')
            {
                // If that next letter is a quote
                if (nextLetter == '"' && mode == CSVMode.OPENED_QUOTE)
                {
                    // Then this quote is escaped and should be added to the content

                    content += letter;

                    // Skip the escape character
                    i++;
                    continue;
                }
                else
                {
                    // otherwise its not an escaped quote and is an opening or closing one
                    // Character is skipped

                    // If it was open, then close it
                    if (mode == CSVMode.OPENED_QUOTE)
                    {
                        results.Add(content);

                        // reset the content
                        content = "";

                        mode = CSVMode.CLOSED;

                        // If there is a next letter available
                        if (nextLetter != '\0')
                        {
                            // If it is a comma
                            if (nextLetter == ',')
                            {
                                i++;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Expected comma. Found: " + nextLetter);
                            }
                        }
                    }
                    else if (mode == CSVMode.OPENED_RAW)
                    {
                        // If it was opened raw, then just add the quote 
                        content += letter;
                    }
                    else if (mode == CSVMode.CLOSED)
                    {
                        // Otherwise open it as a quote 

                        mode = CSVMode.OPENED_QUOTE;
                    }
                }
            }
            // If its a comma seperator
            else if (letter == ',')
            {
                // If in quote mode
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    // Just read it
                    content += letter;
                }
                // If raw, then close the content
                else if (mode == CSVMode.OPENED_RAW)
                {
                    results.Add(content);

                    content = "";

                    mode = CSVMode.CLOSED;
                }
                // If it was closed, then open it raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    results.Add(content);

                    content = "";
                }
            }
            else
            {
                // If opened quote, just read it
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    content += letter;
                }
                // If opened raw, then read it
                else if (mode == CSVMode.OPENED_RAW)
                {
                    content += letter;
                }
                // It closed, then open raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    content += letter;
                }
            }
        }

        // If it was still reading when the buffer finished
        if (mode != CSVMode.CLOSED)
        {
            results.Add(content);
        }

        return results;
    }
John
sumber
0

Berikut ini adalah solusi singkat dan sederhana.

                using (TextFieldParser parser = new TextFieldParser(outputLocation))
                 {
                        parser.TextFieldType = FieldType.Delimited;
                        parser.SetDelimiters(",");
                        string[] headers = parser.ReadLine().Split(',');
                        foreach (string header in headers)
                        {
                            dataTable.Columns.Add(header);
                        }
                        while (!parser.EndOfData)
                        {
                            string[] fields = parser.ReadFields();
                            dataTable.Rows.Add(fields);
                        }
                    }
Elang
sumber