Cara membaca file CSV ke dalam .NET Datatable

170

Bagaimana saya bisa memuat file CSV ke dalam System.Data.DataTable, membuat data yang berdasarkan pada file CSV?

Apakah fungsi ADO.net biasa memungkinkan ini?

Ronnie Overby
sumber
21
Bagaimana ini mungkin 'di luar topik'? Ini pertanyaan khusus dan 100 orang merasa berguna
Ryan
10
@Ryan: Sesungguhnya aku berkata kepadamu ... StackOverflow moderator adalah induk dari ular berbisa. Dapatkan di belakang saya, moderator StackOverflow!
Ronnie Overby

Jawaban:

89

Berikut adalah kelas yang sangat baik yang akan menyalin data CSV ke dalam datatable menggunakan struktur data untuk membuat DataTable:

Pengurai generik portabel dan efisien untuk file datar

Mudah dikonfigurasikan dan mudah digunakan. Saya mendorong Anda untuk melihatnya.

Jay Riggs
sumber
Sangat bagus. Ini bekerja dengan baik bagi saya di luar kotak, bahkan tanpa membaca dokumentasi.
Senyum
Apakah ini akan berfungsi pada file CSV di mana setiap baris mungkin memiliki struktur yang berbeda? Saya memiliki file log dengan berbagai jenis acara log yang perlu dipisahkan menjadi beberapa tabel.
gonzobrains
2
@gonzobrains - Mungkin tidak; asumsi dasar file CSV adalah struktur data persegi panjang berdasarkan satu set header kolom yang ditentukan pada baris pertama. Apa yang Anda miliki tampaknya lebih umum dibatasi koma, data terdiskriminasi, membutuhkan "ETL" yang lebih canggih untuk mem-parsing dari file ke instance objek dari berbagai jenis (yang dapat mencakup DataRows dari berbagai Tabel Data).
KeithS
93

Saya telah menggunakan OleDbprovider. Namun, itu memiliki masalah jika Anda membaca dalam baris yang memiliki nilai numerik tetapi Anda ingin mereka diperlakukan sebagai teks. Namun, Anda dapat mengatasi masalah itu dengan membuat schema.inifile. Inilah metode yang saya gunakan:

// using System.Data;
// using System.Data.OleDb;
// using System.Globalization;
// using System.IO;

static DataTable GetDataTableFromCsv(string path, bool isFirstRowHeader)
{
    string header = isFirstRowHeader ? "Yes" : "No";

    string pathOnly = Path.GetDirectoryName(path);
    string fileName = Path.GetFileName(path);

    string sql = @"SELECT * FROM [" + fileName + "]";

    using(OleDbConnection connection = new OleDbConnection(
              @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly + 
              ";Extended Properties=\"Text;HDR=" + header + "\""))
    using(OleDbCommand command = new OleDbCommand(sql, connection))
    using(OleDbDataAdapter adapter = new OleDbDataAdapter(command))
    {
        DataTable dataTable = new DataTable();
        dataTable.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(dataTable);
        return dataTable;
    }
}
Jim Scott
sumber
Terimakasih kawan. Itu membantu saya. Saya memiliki file CSV di mana koma bukan hanya pemisah, mereka ada di mana-mana di dalam banyak nilai kolom, jadi membuat regex yang akan membagi garis agak sulit. OleDbProvider menyimpulkan skema dengan benar.
Galilyou
Implementasinya masuk akal tetapi bagaimana kita menangani sel yang berisi tipe data campuran. Misalnya, 40C dan lain-lain?
GKED
GKED, jika data yang Anda baca selalu memiliki kumpulan kolom dan jenis yang Anda harapkan, Anda dapat menempatkan di folder yang sama file shema.ini yang memberi tahu informasi penyedia OleDb tentang kolom. Berikut ini adalah tautan ke artikel Microsoft yang menyediakan detail tentang bagaimana menyusun file. msdn.microsoft.com/en-us/library/…
Jim Scott
4
Sementara jawaban ini akan berhasil, saya akan sangat menyarankan untuk tidak melakukannya. Anda memperkenalkan ketergantungan eksternal yang mungkin bertentangan dengan instalasi kantor lainnya pada mesin yang sama (menggunakan Excel di lingkungan pengembang lokal Anda?), Tergantung pada versi yang diinstal. Ada paket NuGet di luar sana (ExcelDataReader, CsvHelper) yang melakukan ini dengan cara yang lebih efisien dan lebih portabel.
A. Murray
1
@ A.Murray - Apa maksud Anda sebenarnya? Ini menggunakan penyedia OleDb bawaan di System.Data.dll. Anda tidak perlu menginstal "driver" tambahan. Dan saya akan terkejut pada hari ini dan usia jika instalasi windows tidak memiliki driver Jet dasar diinstal. Ini CSV tahun 1990 ....
Paul Easter
40

Saya telah memutuskan untuk menggunakan Csv Reader Sebastien Lorion .

Saran Jay Riggs juga merupakan solusi yang hebat, tetapi saya tidak membutuhkan semua fitur yang disediakan Generic Parser dari Andrew Rissing .

UPDATE 10/25/2010

Setelah menggunakan Pembaca Csv Sebastien Lorion dalam proyek saya selama hampir satu setengah tahun, saya telah menemukan bahwa ia melempar pengecualian ketika mem-parsing beberapa file csv yang saya yakini terbentuk dengan baik.

Jadi, saya beralih ke Generic Parser karya Andrew Rissing dan sepertinya akan jauh lebih baik.

UPDATE 9/22/2014

Saat ini, saya lebih banyak menggunakan metode ekstensi ini untuk membaca teks yang dibatasi:

https://github.com/Core-Techs/Common/blob/master/CoreTechs.Common/Text/DelimitedTextExtensions.cs#L22

https://www.nuget.org/packages/CoreTechs.Common/

UPDATE 2/20/2015

Contoh:

var csv = @"Name, Age
Ronnie, 30
Mark, 40
Ace, 50";

TextReader reader = new StringReader(csv);
var table = new DataTable();
using(var it = reader.ReadCsvWithHeader().GetEnumerator())
{

    if (!it.MoveNext()) return;

    foreach (var k in it.Current.Keys)
        table.Columns.Add(k);

    do
    {
        var row = table.NewRow();
        foreach (var k in it.Current.Keys)
            row[k] = it.Current[k];
    
        table.Rows.Add(row);
    
    } while (it.MoveNext());
}
Ronnie Overby
sumber
Saya setuju bahwa pembaca CSV Sebastien Lorien hebat. Saya menggunakannya untuk pemrosesan CSV yang berat, tetapi saya juga menggunakan Andrew's Rissing untuk pekerjaan kecil dan ini sangat membantu saya. Selamat bersenang-senang!
Jay Riggs
Bagaimana saya bisa menggunakan kelas ini untuk memuat CSV ke DATATABLE?
Muflix
Saya mencoba ini tetapi koleksi it.Current.Keys kembali dengan "System.Linq.Enumerable + WhereSelectListIterator`2 [System.Int32, System.Char]" daripada nama kolom. Ada pemikiran mengapa?
user3658298
Bisakah Anda menggunakan pembatas multi-karakter?
menggulung
Tidak, tapi saya berpikir untuk mengaktifkannya.
Ronnie Overby
32

Hei ini bekerja 100%

  public 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;
   }

Gambar CSV masukkan deskripsi gambar di sini

Tabel data Diimpor masukkan deskripsi gambar di sini

Shivam Srivastava
sumber
7
Hanya ketika 100% dari input adalah yang paling sederhana dari file CSV (yang mungkin benar dalam kasus Anda).
Ronnie Overby
Anda benar. Anda harus menggunakan codeproject.com/Articles/9258/A-Fast-CSV-Reader (Lorion dll) saya mencoba nya bekerja dengan baik.
Shivam Srivastava
1
Lihat jawaban saya dari 2009.
Ronnie Overby
1
@ShivamSrivastava Saya mendapatkan kesalahan di baris terakhir apakah Anda di sana kemudian memberi Anda info kontak lainnya
Sunil Acharya
Meskipun saya tidak menggunakan versi ini persis, itu didasarkan pada itu saya menyelesaikan masalah saya. Terima kasih. Bekerja dengan sangat baik.
nrod
13

Kami selalu menggunakan driver Jet.OLEDB, sampai kami mulai membuka aplikasi 64 bit. Microsoft belum dan tidak akan merilis driver Jet 64 bit. Berikut ini adalah solusi sederhana yang kami buat dengan menggunakan File.ReadAllLines dan String.Split untuk membaca dan mem-parsing file CSV dan secara manual memuat DataTable. Seperti disebutkan di atas, itu TIDAK menangani situasi di mana salah satu nilai kolom berisi koma. Kami menggunakan ini sebagian besar untuk membaca file konfigurasi khusus - bagian yang menyenangkan tentang menggunakan file CSV adalah kami dapat mengeditnya di Excel.

string CSVFilePathName = @"C:\test.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
    dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 1; i < Lines.GetLength(0); i++)
{
    Fields = Lines[i].Split(new char[] { ',' });
    Row = dt.NewRow();
    for (int f = 0; f < Cols; f++)
        Row[f] = Fields[f];
    dt.Rows.Add(Row);
}
Chuck Bevitt
sumber
8

ini adalah kode yang saya gunakan tetapi aplikasi Anda harus berjalan dengan versi net 3.5

private void txtRead_Click(object sender, EventArgs e)
        {
           // var filename = @"d:\shiptest.txt";

            openFileDialog1.InitialDirectory = "d:\\";
            openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            DialogResult result = openFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    var reader = ReadAsLines(openFileDialog1.FileName);

                    var data = new DataTable();

                    //this assume the first record is filled with the column names
                    var headers = reader.First().Split(',');
                    foreach (var header in headers)
                    {
                        data.Columns.Add(header);
                    }

                    var records = reader.Skip(1);
                    foreach (var record in records)
                    {
                        data.Rows.Add(record.Split(','));
                    }

                    dgList.DataSource = data;
                }
            }
        }

        static IEnumerable<string> ReadAsLines(string filename)
        {
            using (StreamReader reader = new StreamReader(filename))
                while (!reader.EndOfStream)
                    yield return reader.ReadLine();
        }
Thomas
sumber
Inilah yang ingin saya sampaikan.
Kapten Kenpachi
8

Anda dapat mencapainya dengan menggunakan Microsoft.VisualBasic.FileIO.TextFieldParser dll di C #

static void Main()
        {
            string csv_file_path=@"C:\Users\Administrator\Desktop\test.csv";

            DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);

            Console.WriteLine("Rows count:" + csvData.Rows.Count);

            Console.ReadLine();
        }


private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();

            try
            {

            using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                {
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = true;
                    string[] colFields = csvReader.ReadFields();
                    foreach (string column in colFields)
                    {
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                    }

                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return csvData;
        }
kombsh
sumber
Tolong jangan mencoba untuk menemukan kembali roda dengan pemrosesan CSV. Ada begitu banyak alternatif open source di luar sana yang sangat kuat.
Mike Cole
1
Terima kasih Brad, tip bermanfaat dalam kaitannya dengan TextFieldParser untuk menangani kutipan yang disematkan.
mattpm
3
public class Csv
{
    public static DataTable DataSetGet(string filename, string separatorChar, out List<string> errors)
    {
        errors = new List<string>();
        var table = new DataTable("StringLocalization");
        using (var sr = new StreamReader(filename, Encoding.Default))
        {
            string line;
            var i = 0;
            while (sr.Peek() >= 0)
            {
                try
                {
                    line = sr.ReadLine();
                    if (string.IsNullOrEmpty(line)) continue;
                    var values = line.Split(new[] {separatorChar}, StringSplitOptions.None);
                    var row = table.NewRow();
                    for (var colNum = 0; colNum < values.Length; colNum++)
                    {
                        var value = values[colNum];
                        if (i == 0)
                        {
                            table.Columns.Add(value, typeof (String));
                        }
                        else
                        {
                            row[table.Columns[colNum]] = value;
                        }
                    }
                    if (i != 0) table.Rows.Add(row);
                }
                catch(Exception ex)
                {
                    errors.Add(ex.Message);
                }
                i++;
            }
        }
        return table;
    }
}
Nodir
sumber
3

Saya menemukan potongan kode ini yang menggunakan Linq dan regex untuk mem-parsing file CSV. Artikel referensi sekarang sudah berusia lebih dari satu setengah tahun, tetapi belum menemukan cara yang lebih rapi untuk mem-parsing CSV menggunakan Linq (dan regex) daripada ini. Peringatannya adalah regex yang diterapkan di sini adalah untuk file yang dibatasi koma (akan mendeteksi tanda koma di dalam tanda kutip!) Dan mungkin tidak perlu dengan baik untuk header, tetapi ada cara untuk mengatasi ini). Ambil puncak:

Dim lines As String() = System.IO.File.ReadAllLines(strCustomerFile)
Dim pattern As String = ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"
Dim r As System.Text.RegularExpressions.Regex = New System.Text.RegularExpressions.Regex(pattern)
Dim custs = From line In lines _
            Let data = r.Split(line) _
                Select New With {.custnmbr = data(0), _
                                 .custname = data(1)}
For Each cust In custs
    strCUSTNMBR = Replace(cust.custnmbr, Chr(34), "")
    strCUSTNAME = Replace(cust.custname, Chr(34), "")
Next
Nepa
sumber
3

Opsi terbaik yang saya temukan, dan itu menyelesaikan masalah di mana Anda mungkin memiliki versi Office yang berbeda diinstal, dan juga masalah 32/64-bit seperti yang disebutkan Chuck Bevitt , adalah FileHelpers .

Itu dapat ditambahkan ke referensi proyek Anda menggunakan NuGet dan itu memberikan solusi satu-liner:

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);
Neo
sumber
bisakah kamu tahu apa itu CommonEngine? Apakah NuGet sama dengan NuGet.Core. Saya hanya menemukan NuGet.Core dalam referensi
sindhu jampani
FileHelpers yang Anda butuhkan. Jika Anda memiliki NuGet, tambahkan dengan NuGet. Jika tidak, tambahkan saja sebagai rakitan di proyek Anda. CommonEngine adalah bagian dari FileHelpers.
Neo
3

Bagi Anda yang ingin tidak menggunakan perpustakaan eksternal, dan memilih untuk tidak menggunakan OleDB, lihat contoh di bawah ini. Semua yang saya temukan adalah OleDB, perpustakaan eksternal, atau hanya pemisahan berdasarkan koma! Untuk kasus saya OleDB tidak berfungsi jadi saya ingin sesuatu yang berbeda.

Saya menemukan sebuah artikel oleh MarkJ yang mereferensikan metode Microsoft.VisualBasic.FileIO.TextFieldParser seperti yang terlihat di sini . Artikel ini ditulis dalam VB dan tidak menghasilkan data, jadi lihat contoh saya di bawah ini.

public static DataTable LoadCSV(string path, bool hasHeader)
    {
        DataTable dt = new DataTable();

        using (var MyReader = new Microsoft.VisualBasic.FileIO.TextFieldParser(path))
        {
            MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
            MyReader.Delimiters = new String[] { "," };

            string[] currentRow;

            //'Loop through all of the fields in the file.  
            //'If any lines are corrupt, report an error and continue parsing.  
            bool firstRow = true;
            while (!MyReader.EndOfData)
            {
                try
                {
                    currentRow = MyReader.ReadFields();

                    //Add the header columns
                    if (hasHeader && firstRow)
                    {
                        foreach (string c in currentRow)
                        {
                            dt.Columns.Add(c, typeof(string));
                        }

                        firstRow = false;
                        continue;
                    }

                    //Create a new row
                    DataRow dr = dt.NewRow();
                    dt.Rows.Add(dr);

                    //Loop thru the current line and fill the data out
                    for(int c = 0; c < currentRow.Count(); c++)
                    {
                        dr[c] = currentRow[c];
                    }
                }
                catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
                {
                    //Handle the exception here
                }
            }
        }

        return dt;
    }
Smeiff
sumber
3

Jawaban yang sangat mendasar: jika Anda tidak memiliki csv kompleks yang dapat menggunakan fungsi pemisahan sederhana ini akan berfungsi dengan baik untuk mengimpor (perhatikan impor ini sebagai string, saya melakukan konversi datatype nanti jika saya perlu)

 private DataTable csvToDataTable(string fileName, char splitCharacter)
    {                
        StreamReader sr = new StreamReader(fileName);
        string myStringRow = sr.ReadLine();
        var rows = myStringRow.Split(splitCharacter);
        DataTable CsvData = new DataTable();
        foreach (string column in rows)
        {
            //creates the columns of new datatable based on first row of csv
            CsvData.Columns.Add(column);
        }
        myStringRow = sr.ReadLine();
        while (myStringRow != null)
        {
            //runs until string reader returns null and adds rows to dt 
            rows = myStringRow.Split(splitCharacter);
            CsvData.Rows.Add(rows);
            myStringRow = sr.ReadLine();
        }
        sr.Close();
        sr.Dispose();
        return CsvData;
    }

Metode saya jika saya mengimpor tabel dengan pemisah string [] dan menangani masalah di mana baris saat ini yang saya baca mungkin telah pergi ke baris berikutnya dalam file csv atau teks <- DALAM hal ini saya ingin mengulang sampai saya mendapatkan ke jumlah total baris di baris pertama (kolom)

public static DataTable ImportCSV(string fullPath, string[] sepString)
    {
        DataTable dt = new DataTable();
        using (StreamReader sr = new StreamReader(fullPath))
        {
           //stream uses using statement because it implements iDisposable
            string firstLine = sr.ReadLine();
            var headers = firstLine.Split(sepString, StringSplitOptions.None);
            foreach (var header in headers)
            {
               //create column headers
                dt.Columns.Add(header);
            }
            int columnInterval = headers.Count();
            string newLine = sr.ReadLine();
            while (newLine != null)
            {
                //loop adds each row to the datatable
                var fields = newLine.Split(sepString, StringSplitOptions.None); // csv delimiter    
                var currentLength = fields.Count();
                if (currentLength < columnInterval)
                {
                    while (currentLength < columnInterval)
                    {
                       //if the count of items in the row is less than the column row go to next line until count matches column number total
                        newLine += sr.ReadLine();
                        currentLength = newLine.Split(sepString, StringSplitOptions.None).Count();
                    }
                    fields = newLine.Split(sepString, StringSplitOptions.None);
                }
                if (currentLength > columnInterval)
                {  
                    //ideally never executes - but if csv row has too many separators, line is skipped
                    newLine = sr.ReadLine();
                    continue;
                }
                dt.Rows.Add(fields);
                newLine = sr.ReadLine();
            }
            sr.Close();
        }

        return dt;
    }
Matt Farguson
sumber
Bagusnya Anda belum mendeklarasikan baris sebagai string [].
Gaya Hewan
@AnimalStyle Anda benar - diperbarui dengan metode yang lebih kuat dan baris yang dideklarasikan
Matt Farguson
3

Diubah dari Bp ChuckBevitt

Solusi kerja:

string CSVFilePathName = APP_PATH + "Facilities.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols-1; i++)
        dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 0; i < Lines.GetLength(0)-1; i++)
{
        Fields = Lines[i].Split(new char[] { ',' });
        Row = dt.NewRow();
        for (int f = 0; f < Cols-1; f++)
                Row[f] = Fields[f];
        dt.Rows.Add(Row);
}
Balaji Selvarajan
sumber
Jadi ini memecahkan masalah memori bukan? Ini adalah pemrosesan baris demi baris dan tidak bertahan dalam memori sehingga tidak boleh ada pengecualian? Saya suka cara ini diproses tetapi tidak File.ReadAllLines () menyimpan semua ke dalam memori? Saya pikir Anda seharusnya menggunakan File.ReadLines () untuk menghindari buffer memori yang besar? Ini adalah jawaban yang bagus untuk pertanyaan yang ada. Saya hanya ingin tahu tentang masalah ingatan.
DtechNet
2

Inilah solusi yang menggunakan driver teks ODBC ADO.Net:

Dim csvFileFolder As String = "C:\YourFileFolder"
Dim csvFileName As String = "YourFile.csv"

'Note that the folder is specified in the connection string,
'not the file. That's specified in the SELECT query, later.
Dim connString As String = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" _
    & csvFileFolder & ";Extended Properties=""Text;HDR=No;FMT=Delimited"""
Dim conn As New Odbc.OdbcConnection(connString)

'Open a data adapter, specifying the file name to load
Dim da As New Odbc.OdbcDataAdapter("SELECT * FROM [" & csvFileName & "]", conn)
'Then fill a data table, which can be bound to a grid
Dim dt As New DataTableda.Fill(dt)

grdCSVData.DataSource = dt

Setelah diisi, Anda dapat menilai properti yang bisa datatable, seperti ColumnName, untuk memanfaatkan semua kekuatan objek data ADO.Net.

Dalam VS2008 Anda dapat menggunakan Linq untuk mencapai efek yang sama.

CATATAN: Ini mungkin duplikat dari pertanyaan SO ini .

Bob Mc
sumber
2

Tidak dapat menahan menambahkan putaran saya sendiri ke ini. Ini jauh lebih baik dan lebih kompak daripada apa yang saya gunakan di masa lalu.

Solusi ini:

  • Tidak bergantung pada driver database atau perpustakaan pihak ke-3.
  • Tidak akan gagal pada nama kolom rangkap
  • Menangani koma dalam data
  • Menangani pembatas apa pun, tidak hanya koma (meskipun itu adalah default)

Inilah yang saya pikirkan:

  Public Function ToDataTable(FileName As String, Optional Delimiter As String = ",") As DataTable
    ToDataTable = New DataTable
    Using TextFieldParser As New Microsoft.VisualBasic.FileIO.TextFieldParser(FileName) With
      {.HasFieldsEnclosedInQuotes = True, .TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited, .TrimWhiteSpace = True}
      With TextFieldParser
        .SetDelimiters({Delimiter})
        .ReadFields.ToList.Unique.ForEach(Sub(x) ToDataTable.Columns.Add(x))
        ToDataTable.Columns.Cast(Of DataColumn).ToList.ForEach(Sub(x) x.AllowDBNull = True)
        Do Until .EndOfData
          ToDataTable.Rows.Add(.ReadFields.Select(Function(x) Text.BlankToNothing(x)).ToArray)
        Loop
      End With
    End Using
  End Function

Itu tergantung pada metode ekstensi ( Unique) untuk menangani nama kolom duplikat yang akan ditemukan sebagai jawaban saya di Cara menambahkan nomor unik ke daftar string

Dan inilah BlankToNothingfungsi pembantu:

  Public Function BlankToNothing(ByVal Value As String) As Object 
    If String.IsNullOrEmpty(Value) Then Return Nothing
    Return Value
  End Function
toddmo
sumber
2

Dengan Cinchoo ETL - pustaka sumber terbuka, Anda dapat dengan mudah mengkonversi file CSV ke DataTable dengan beberapa baris kode.

using (var p = new ChoCSVReader(** YOUR CSV FILE **)
     .WithFirstLineHeader()
    )
{
    var dt = p.AsDataTable();
}

Untuk informasi lebih lanjut, silakan kunjungi proyek kode artikel .

Semoga ini bisa membantu.

RajN
sumber
2
    private static DataTable LoadCsvData(string refPath)
    {
        var cfg = new Configuration() { Delimiter = ",", HasHeaderRecord = true };
        var result = new DataTable();
        using (var sr = new StreamReader(refPath, Encoding.UTF8, false, 16384 * 2))
        {
            using (var rdr = new CsvReader(sr, cfg))
            using (var dataRdr = new CsvDataReader(rdr))
            {
                result.Load(dataRdr);
            }
        }
        return result;
    }

menggunakan: https://joshclose.github.io/CsvHelper/

Youssef Bouha
sumber
Perhatikan bahwa dalam rilis 13 Configuration diubah namanya menjadi CsvConfiguration untuk menghindari konflik namespace. Demo jawaban ini berfungsi: dotnetfiddle.net/sdwc6i
dbc
2

Saya menggunakan perpustakaan bernama ExcelDataReader, Anda dapat menemukannya di NuGet. Pastikan untuk menginstal ekstensi ExcelDataReader dan ExcelDataReader.DataSet (yang terakhir menyediakan metode AsDataSet yang diperlukan yang dirujuk di bawah).

Saya merangkum semuanya dalam satu fungsi, Anda dapat menyalinnya dalam kode Anda secara langsung. Berikan path ke file CSV, itu memberi Anda dataset dengan satu tabel.

public static DataSet GetDataSet(string filepath)
{
   var stream = File.OpenRead(filepath);

   try
   {
       var reader = ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration()
       {
           LeaveOpen = false
       });

       var result = reader.AsDataSet(new ExcelDataSetConfiguration()
       {
           // Gets or sets a value indicating whether to set the DataColumn.DataType 
           // property in a second pass.
           UseColumnDataType = true,

           // Gets or sets a callback to determine whether to include the current sheet
           // in the DataSet. Called once per sheet before ConfigureDataTable.
           FilterSheet = (tableReader, sheetIndex) => true,

           // Gets or sets a callback to obtain configuration options for a DataTable. 
           ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
           {
               // Gets or sets a value indicating the prefix of generated column names.
               EmptyColumnNamePrefix = "Column",

               // Gets or sets a value indicating whether to use a row from the 
               // data as column names.
               UseHeaderRow = true,

               // Gets or sets a callback to determine which row is the header row. 
               // Only called when UseHeaderRow = true.
               ReadHeaderRow = (rowReader) =>
               {
                   // F.ex skip the first row and use the 2nd row as column headers:
                   //rowReader.Read();
               },

               // Gets or sets a callback to determine whether to include the 
               // current row in the DataTable.
               FilterRow = (rowReader) =>
               {
                   return true;
               },

               // Gets or sets a callback to determine whether to include the specific
               // column in the DataTable. Called once per column after reading the 
               // headers.
               FilterColumn = (rowReader, columnIndex) =>
               {
                   return true;
               }
           }
       });

       return result;
   }
   catch (Exception ex)
   {
       return null;
   }
   finally
   {
       stream.Close();
       stream.Dispose();
   }
}
Dotnetsqlcoder
sumber
Ini tahun 2020 dan ini adalah solusi hebat dibandingkan dengan beberapa jawaban lama di sini. Ini dikemas dengan baik dan menggunakan perpustakaan populer dan ringan dari NuGet. Dan fleksibel - jika CSV Anda ada di memori, cukup letakkan sebagai MemoryStreamganti jalur file. DataTable yang diminta OP mudah diekstraksi dari DataSet seperti ini:result.Tables[0]
Tawab Wakil
1

Cukup membagikan metode ekstensi ini, saya harap ini dapat membantu seseorang.

public static List<string> ToCSV(this DataSet ds, char separator = '|')
{
    List<string> lResult = new List<string>();

    foreach (DataTable dt in ds.Tables)
    {
        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(separator.ToString(), columnNames));

        foreach (DataRow row in dt.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field =>
              string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
            sb.AppendLine(string.Join(separator.ToString(), fields));
        }

        lResult.Add(sb.ToString());
    }
    return lResult;
}

public static DataSet CSVtoDataSet(this List<string> collectionCSV, char separator = '|')
{
    var ds = new DataSet();

    foreach (var csv in collectionCSV)
    {
        var dt = new DataTable();

        var readHeader = false;
        foreach (var line in csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
        {
            if (!readHeader)
            {
                foreach (var c in line.Split(separator))
                    dt.Columns.Add(c);
            }
            else
            {
                dt.Rows.Add(line.Split(separator));
            }
        }

        ds.Tables.Add(dt);
    }

    return ds;
}
Akaize
sumber
0

Gunakan ini, satu fungsi menyelesaikan semua masalah koma dan kutipan:

public static DataTable CsvToDataTable(string strFilePath)
    {

        if (File.Exists(strFilePath))
        {

            string[] Lines;
            string CSVFilePathName = strFilePath;

            Lines = File.ReadAllLines(CSVFilePathName);
            while (Lines[0].EndsWith(","))
            {
                Lines[0] = Lines[0].Remove(Lines[0].Length - 1);
            }
            string[] Fields;
            Fields = Lines[0].Split(new char[] { ',' });
            int Cols = Fields.GetLength(0);
            DataTable dt = new DataTable();
            //1st row must be column names; force lower case to ensure matching later on.
            for (int i = 0; i < Cols; i++)
                dt.Columns.Add(Fields[i], typeof(string));
            DataRow Row;
            int rowcount = 0;
            try
            {
                string[] ToBeContinued = new string[]{};
                bool lineToBeContinued = false;
                for (int i = 1; i < Lines.GetLength(0); i++)
                {
                    if (!Lines[i].Equals(""))
                    {
                        Fields = Lines[i].Split(new char[] { ',' });
                        string temp0 = string.Join("", Fields).Replace("\"\"", "");
                        int quaotCount0 = temp0.Count(c => c == '"');
                        if (Fields.GetLength(0) < Cols || lineToBeContinued || quaotCount0 % 2 != 0)
                        {
                            if (ToBeContinued.GetLength(0) > 0)
                            {
                                ToBeContinued[ToBeContinued.Length - 1] += "\n" + Fields[0];
                                Fields = Fields.Skip(1).ToArray();
                            }
                            string[] newArray = new string[ToBeContinued.Length + Fields.Length];
                            Array.Copy(ToBeContinued, newArray, ToBeContinued.Length);
                            Array.Copy(Fields, 0, newArray, ToBeContinued.Length, Fields.Length);
                            ToBeContinued = newArray;
                            string temp = string.Join("", ToBeContinued).Replace("\"\"", "");
                            int quaotCount = temp.Count(c => c == '"');
                            if (ToBeContinued.GetLength(0) >= Cols && quaotCount % 2 == 0 )
                            {
                                Fields = ToBeContinued;
                                ToBeContinued = new string[] { };
                                lineToBeContinued = false;
                            }
                            else
                            {
                                lineToBeContinued = true;
                                continue;
                            }
                        }

                        //modified by Teemo @2016 09 13
                        //handle ',' and '"'
                        //Deserialize CSV following Excel's rule:
                        // 1: If there is commas in a field, quote the field.
                        // 2: Two consecutive quotes indicate a user's quote.

                        List<int> singleLeftquota = new List<int>();
                        List<int> singleRightquota = new List<int>();

                        //combine fileds if number of commas match
                        if (Fields.GetLength(0) > Cols) 
                        {
                            bool lastSingleQuoteIsLeft = true;
                            for (int j = 0; j < Fields.GetLength(0); j++)
                            {
                                bool leftOddquota = false;
                                bool rightOddquota = false;
                                if (Fields[j].StartsWith("\"")) 
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    foreach (char c in Fields[j]) //start with how many "
                                    {
                                        if (c == '"')
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    if (numberOfConsecutiveQuotes % 2 == 1)//start with odd number of quotes indicate system quote
                                    {
                                        leftOddquota = true;
                                    }
                                }

                                if (Fields[j].EndsWith("\""))
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    for (int jj = Fields[j].Length - 1; jj >= 0; jj--)
                                    {
                                        if (Fields[j].Substring(jj,1) == "\"") // end with how many "
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }

                                    if (numberOfConsecutiveQuotes % 2 == 1)//end with odd number of quotes indicate system quote
                                    {
                                        rightOddquota = true;
                                    }
                                }
                                if (leftOddquota && !rightOddquota)
                                {
                                    singleLeftquota.Add(j);
                                    lastSingleQuoteIsLeft = true;
                                }
                                else if (!leftOddquota && rightOddquota)
                                {
                                    singleRightquota.Add(j);
                                    lastSingleQuoteIsLeft = false;
                                }
                                else if (Fields[j] == "\"") //only one quota in a field
                                {
                                    if (lastSingleQuoteIsLeft)
                                    {
                                        singleRightquota.Add(j);
                                    }
                                    else
                                    {
                                        singleLeftquota.Add(j);
                                    }
                                }
                            }
                            if (singleLeftquota.Count == singleRightquota.Count)
                            {
                                int insideCommas = 0;
                                for (int indexN = 0; indexN < singleLeftquota.Count; indexN++)
                                {
                                    insideCommas += singleRightquota[indexN] - singleLeftquota[indexN];
                                }
                                if (Fields.GetLength(0) - Cols >= insideCommas) //probabaly matched
                                {
                                    int validFildsCount = insideCommas + Cols; //(Fields.GetLength(0) - insideCommas) may be exceed the Cols
                                    String[] temp = new String[validFildsCount];
                                    int totalOffSet = 0;
                                    for (int iii = 0; iii < validFildsCount - totalOffSet; iii++)
                                    {
                                        bool combine = false;
                                        int storedIndex = 0;
                                        for (int iInLeft = 0; iInLeft < singleLeftquota.Count; iInLeft++)
                                        {
                                            if (iii + totalOffSet == singleLeftquota[iInLeft])
                                            {
                                                combine = true;
                                                storedIndex = iInLeft;
                                                break;
                                            }
                                        }
                                        if (combine)
                                        {
                                            int offset = singleRightquota[storedIndex] - singleLeftquota[storedIndex];
                                            for (int combineI = 0; combineI <= offset; combineI++)
                                            {
                                                temp[iii] += Fields[iii + totalOffSet + combineI] + ",";
                                            }
                                            temp[iii] = temp[iii].Remove(temp[iii].Length - 1, 1);
                                            totalOffSet += offset;
                                        }
                                        else
                                        {
                                            temp[iii] = Fields[iii + totalOffSet];
                                        }
                                    }
                                    Fields = temp;
                                }
                            }
                        }
                        Row = dt.NewRow();
                        for (int f = 0; f < Cols; f++)
                        {
                            Fields[f] = Fields[f].Replace("\"\"", "\""); //Two consecutive quotes indicate a user's quote
                            if (Fields[f].StartsWith("\""))
                            {
                                if (Fields[f].EndsWith("\""))
                                {
                                    Fields[f] = Fields[f].Remove(0, 1);
                                    if (Fields[f].Length > 0)
                                    {
                                        Fields[f] = Fields[f].Remove(Fields[f].Length - 1, 1);
                                    }
                                }
                            }
                            Row[f] = Fields[f];
                        }
                        dt.Rows.Add(Row);
                        rowcount++;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception( "row: " + (rowcount+2) + ", " + ex.Message);
            }
            //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", FilePath + FileName));
            //OleDbCommand command = new OleDbCommand("SELECT * FROM " + FileName, connection);
            //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
            //DataTable dt = new DataTable();
            //adapter.Fill(dt);
            //adapter.Dispose();
            return dt;
        }
        else
            return null;

        //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", strFilePath));
        //OleDbCommand command = new OleDbCommand("SELECT * FROM " + strFileName, connection);
        //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
        //DataTable dt = new DataTable();
        //adapter.Fill(dt);
        //return dt;
    }
Teemo
sumber
0
 Public Function ReadCsvFileToDataTable(strFilePath As String) As DataTable
    Dim dtCsv As DataTable = New DataTable()
    Dim Fulltext As String
    Using sr As StreamReader = New StreamReader(strFilePath)
        While Not sr.EndOfStream
            Fulltext = sr.ReadToEnd().ToString()
            Dim rows As String() = Fulltext.Split(vbLf)
            For i As Integer = 0 To rows.Count() - 1 - 1
                Dim rowValues As String() = rows(i).Split(","c)
                If True Then
                    If i = 0 Then
                        For j As Integer = 0 To rowValues.Count() - 1
                            dtCsv.Columns.Add(rowValues(j))
                        Next
                    Else
                        Dim dr As DataRow = dtCsv.NewRow()
                        For k As Integer = 0 To rowValues.Count() - 1
                            dr(k) = rowValues(k).ToString()
                        Next
                        dtCsv.Rows.Add(dr)
                    End If
                End If
            Next
        End While
    End Using
    Return dtCsv
End Function
Esat Kiziltepe
sumber
0

Saya baru-baru ini menulis parser CSV untuk .NET yang saya klaim saat ini adalah yang tercepat yang tersedia sebagai paket nuget : Sylvan.Data.Csv .

Menggunakan pustaka ini untuk memuat DataTablesangat mudah.

using var tr = File.OpenText("data.csv");
using var dr = CsvDataReader.Create(tr);
var dt = new DataTable();
dt.Load(dr);

Dengan asumsi file Anda adalah file standar yang dipisahkan koma dengan header, itu saja yang Anda butuhkan. Ada juga opsi untuk memungkinkan membaca file tanpa header, dan menggunakan pembatas alternatif dll.

Dimungkinkan juga untuk menyediakan skema khusus untuk file CSV sehingga kolom dapat diperlakukan sebagai sesuatu selain stringnilai. Ini akan memungkinkan DataTablekolom untuk dimuat dengan nilai-nilai yang dapat lebih mudah untuk dikerjakan, karena Anda tidak perlu memaksa mereka ketika Anda mengaksesnya.

var schema = new TypedCsvSchema();
schema.Add(0, typeof(int));
schema.Add(1, typeof(string));
schema.Add(2, typeof(double?));
schema.Add(3, typeof(DateTime));
schema.Add(4, typeof(DateTime?));

var options = new CsvDataReaderOptions { 
    Schema = schema 
};

using var tr = GetData();
using var dr = CsvDataReader.Create(tr, options);

TypedCsvSchemaadalah implementasi ICsvSchemaProvideryang menyediakan cara sederhana untuk menentukan jenis kolom. Namun, juga memungkinkan untuk memberikan kebiasaan ICsvSchemaProvidersaat Anda ingin memberikan lebih banyak metadata, seperti keunikan atau ukuran kolom terbatas, dll.

MarkPflug
sumber