Bir CSV dosyasını .NET Datatable'a okuma


170

CSV dosyasına System.Data.DataTabledayalı veri tablosu oluşturarak bir CSV dosyasını nasıl yükleyebilirim ?

Normal ADO.net işlevselliği buna izin veriyor mu?


21
Bu muhtemelen 'konu dışı' nasıl? Bu belirli bir soru ve 100 kişi yararlı buluyor
Ryan

10
@Ryan: Kesinlikle size söylüyorum ... StackOverflow moderatörleri bir engerek vardır. Arkamı yakala StackOverflow moderatörleri!
Ronnie Overby

Yanıtlar:


89

DataTable'ı oluşturmak için verilerin yapısını kullanarak CSV verilerini bir veri tablosuna kopyalayacak mükemmel bir sınıf:

Düz dosyalar için taşınabilir ve verimli bir genel ayrıştırıcı

Yapılandırması ve kullanımı kolaydır. Sizi bir göz atmaya çağırıyorum.


Gerçekten mükemmel. Belgeleri okumadan, kutudan çıkar çıkmaz mükemmel çalıştı.
smirkingman

Bu, her satırın farklı bir yapıda olabileceği CSV dosyalarında çalışır mı? Birden çok tabloya ayrılması gereken günlüğe olay farklı türleri ile bir günlük dosyası var.
gonzobrains

2
@gonzobrainler - Muhtemelen hayır; bir CSV dosyasının temel varsayımı, ilk satırda belirtilen tek bir sütun başlığı kümesine dayanan dikdörtgen bir veri yapısıdır. Sahip olduğunuz daha genel virgülle ayrılmış, farklılaştırılmış veriler gibi görünüyor ve dosyadan farklı türlerde (farklı DataTable'ların DataRow'lerini içerebilir) nesne örneklerine ayrıştırılması için daha karmaşık "ETL" gerektiriyor.
KeithS

93

Ben OleDbsağlayıcı kullanıyorum . Ancak, sayısal değerleri olan satırlarda okuyorsanız, ancak metin olarak ele alınmasını istiyorsanız sorun yaşar. Ancak, bir schema.inidosya oluşturarak bu sorunu çözebilirsiniz . İşte kullandığım yöntem:

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

Sağol kanka. Bu bana yardımcı oldu. Virgüllerin yalnızca ayırıcılar olmadığı, her yerde birçok sütun değerinin içinde oldukları bir CSV dosyası vardı, bu yüzden satırı bölecek bir regex ile gelmek biraz zor oldu. OleDbProvider şemayı doğru şekilde çıkardı.
Galilyou

Uygulama anlamlıdır, ancak karışık veri türleri içeren hücrelerle nasıl başa çıkacağız. Örneğin, 40C ve benzeri?
GKED

GKED, okuduğunuz verilerin her zaman beklenen sütun ve tür kümesi varsa, aynı klasöre, OleDb sağlayıcısına sütunlar hakkında bilgi veren bir shema.ini dosyası yerleştirebilirsiniz. Dosyanın nasıl yapılandırılacağına dair ayrıntılar sağlayan bir Microsoft makalesinin bağlantısı. msdn.microsoft.com/tr-tr/library/…
Jim Scott

4
Bu cevap işe yarayacak olsa da, şiddetle karşı çıkmanızı şiddetle tavsiye ederim. Yüklü sürümlere bağlı olarak, aynı makinedeki diğer ofis kurulumlarıyla çakışabilecek (yerel geliştirici ortamınızda Excel kullanılsın mı?) Harici bir bağımlılık ortaya koydunuz. Bunu daha verimli, daha taşınabilir yollarla yapan NuGet paketleri (ExcelDataReader, CsvHelper) var.
A. Murray

1
@ A.Murray - Tam olarak ne demek istiyorsun? Bu, System.Data.dll içindeki yerleşik OleDb sağlayıcısını kullanır. Ek bir "sürücü" kurmanıza gerek yoktur. Ve herhangi bir windows kurulumunda temel Jet sürücüsü kurulu olmasaydı bu gün ve yaşta şok olurdum. 1990'ların CSV'si ....
Paul Easter

40

Sebastien Lorion'un Csv Okuyucusunu kullanmaya karar verdim .

Jay Riggs önerisi de harika bir çözüm, ancak Andrew Rissing'in Genel Çözümleyici'nin sağladığı tüm özelliklere ihtiyacım yoktu .

GÜNCELLEME 10/25/2010

Kullandıktan sonra Sebastien Lorion en Csv Reader yaklaşık bir bir buçuk yıl boyunca projemde, ben de oluşturulacak inandığımız bazı csv dosyalarını ayrıştırma zaman istisnalar atar bulduk.

Bu yüzden Andrew Rissing'in Jenerik Ayrıştırıcısına geçtim ve çok daha iyi olduğu görülüyor.

GÜNCELLEME 9/22/2014

Bugünlerde, çoğunlukla bu uzantı yöntemini sınırlandırılmış metni okumak için kullanıyorum:

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

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

GÜNCELLEME 2/20/2015

Misal:

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());
}

Sebastien Lorien'in CSV okuyucusunun harika olduğunu kabul ediyorum. Ağır CSV işleme için kullanıyorum, ama Andrew's Rissing'i küçük işler için de kullandım ve bana iyi hizmet etti. İyi eğlenceler!
Jay Riggs

CSV'i DATATABLE'a yüklemek için bu sınıfları nasıl kullanabilirim?
Muflix

Bunu denedim ama sütun adı yerine it.Current.Keys koleksiyonu "System.Linq.Enumerable + WhereSelectListIterator`2 [System.Int32, System.Char]" ile döner. Neden olduğu hakkında herhangi bir düşünceniz var mı?
user3658298

Çok karakterli sınırlayıcılar kullanabilir misiniz?
rulolar

Hayır, ama bunu etkinleştirmeyi düşündüm.
Ronnie Overby

32

Hey % 100 çalışıyor

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

CSV Resmi resim açıklamasını buraya girin

Veri tablosu İçe aktarıldı resim açıklamasını buraya girin


7
Yalnızca girişlerin% 100'ü en basit CSV dosyaları olduğunda (bu sizin durumunuz için geçerli olabilir).
Ronnie Overby

Haklısın. codeproject.com/Articles/9258/A-Fast-CSV-Reader (Lorion dll) kullanmalıyım .
Shivam Srivastava

1
2009
yanıtımı

1
@ShivamSrivastava Son satırdaki hatayı alıyorum, o zaman size başka iletişim bilgileri veriyor musunuz
Sunil Acharya

Bu sürümü tam olarak kullanmama rağmen, sorunumu çözdüm. Teşekkür ederim. Çok iyi çalışıyor.
17'de nrod

13

64 bit uygulamalara geçene kadar her zaman Jet.OLEDB sürücüsünü kullanırdık. Microsoft 64 bit Jet sürücüsünü yayınlamamıştır ve yayınlamamaktadır. CSV dosyasını okumak ve ayrıştırmak ve bir DataTable'ı manuel olarak yüklemek için File.ReadAllLines ve String.Split kullanan basit bir çözüm. Yukarıda belirtildiği gibi, sütun değerlerinden birinin virgül içerdiği durumu ÇALIŞMAZ. Bunu çoğunlukla özel yapılandırma dosyalarını okumak için kullanıyoruz - CSV dosyalarını kullanmanın güzel yanı, bunları Excel'de düzenleyebilmemiz.

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

8

Bu onu kullanmak kod ama uygulamalarınız net sürüm 3.5 ile çalışması gerekir

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();
        }

Sunmak istediğim şey bu.
Kaptan Kenpachi

8

C # Microsoft.VisualBasic.FileIO.TextFieldParser dll kullanarak elde edebilirsiniz

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

Lütfen tekerleği CSV işleme ile yeniden icat etmeye çalışmayın. Dışarıda çok sağlam olan çok sayıda açık kaynak alternatifi var.
Mike Cole

1
Teşekkürler Brad, gömülü tırnak işleme için TextFieldParser ile ilgili yararlı ipucu.
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;
    }
}

3

Bir CSV dosyasını ayrıştırmak için Linq ve regex kullanan bu kod parçası geldi. Atıfta bulunulan makale şimdi bir buçuk yıldan daha eski, ancak Linq (ve regex) kullanarak bir CSV'yi ayrıştırmak için daha temiz bir yolla karşılaşmadı. Uyarı, burada uygulanan normal ifade virgülle ayrılmış dosyalar içindir (tırnak içindeki virgülleri algılar!) Ve başlıklara iyi gelmeyebilir, ancak bunların üstesinden gelmenin bir yolu vardır). Zirveye çıkın:

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

3

En iyi seçenek buldum ve Office yüklü ait farklı sürümlerine sahip olabilir ve aynı zamanda gibi 32/64-bit sorunları sorunları çözer Chuck Bevitt söz vardır FileHelpers .

NuGet kullanarak proje referanslarınıza eklenebilir ve tek katmanlı bir çözüm sağlar:

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);

CommonEngine nedir söyleyebilirim? NuGet, NuGet.Core ile aynıdır. Referanslarda sadece NuGet.Core buldum
sindhu jampani

İhtiyacınız olan FileHelpers. NuGet'iniz varsa, NuGet ile ekleyin. Aksi takdirde, projenizde bir montaj olarak ekleyin. CommonEngine, FileHelpers'ın bir parçasıdır.
Neo

3

Harici bir kitaplık kullanmak istemeyen ve OleDB'yi kullanmamayı tercih edenler için aşağıdaki örneğe bakın. Bulduğum her şey ya OleDB, harici kütüphane ya da sadece bir virgül dayalı bölme oldu! Benim durumum için OleDB çalışma değildi bu yüzden farklı bir şey istedim.

MarkJ tarafından burada görüldüğü gibi Microsoft.VisualBasic.FileIO.TextFieldParser yöntemine başvuran bir makale buldum . Makale VB'de yazılmıştır ve veri tablosu döndürmez, bu yüzden aşağıdaki örneğime bakın.

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

3

Çok temel cevap: basit bir bölünmüş işlevi kullanabilen karmaşık bir csv yoksa bu içe aktarmak için iyi çalışır (bu dizeler olarak içe aktarıldığını unutmayın, gerekirse daha sonra veri tipi dönüşümleri yapmak)

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

Bir dize [] ayırıcı içeren bir tablo alıyorsanız ve okuduğum satırın csv veya metin dosyasında bir sonraki satıra gitmiş olabileceği sorunu ele alıyorsa benim yöntemim - Bu durumda ben elde edene kadar döngü istiyorum ilk satırdaki toplam satır sayısına (sütunlar) kadar

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

Güzel, henüz satırları [] olarak henüz açıklamadınız.
Hayvan Tarzı

@AnimalStyle haklısınız - daha sağlam bir yöntemle ve açıklanan satırlarla güncellendi
Matt Farguson

3

Bay tarafından değiştirildi ChuckBevitt tarafından değiştirildi

Çalışma çözümü:

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

Yani bu bir hafıza sorununu çözüyor değil mi? Bu satır satır işleme ve bellekte kalıcı değil, bu yüzden istisna olmamalı? Ben bu işlenir yolu seviyorum ama File.ReadAllLines () tüm belleğe kaydetmek değil mi? Büyük bellek arabelleğini önlemek için File.ReadLines () kullanmanız gerektiğini düşünüyorum? Bu sadece hafıza endişeleri hakkında bilmek istiyorum eldeki soru için iyi bir cevap.
DtechNet

2

İşte ADO.Net'in ODBC metin sürücüsünü kullanan bir çözüm:

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

Doldurulduktan sonra, ADO.Net veri nesnelerinin tüm güçlerinden faydalanmak için ColumnName gibi datatable'ın özelliklerine değer verebilirsiniz.

VS2008'de aynı etkiyi elde etmek için Linq kullanabilirsiniz.

NOT: Bu, bu SO sorunun bir kopyası olabilir .


2

Buna kendi spinimi eklemeye direnemiyorum. Bu, geçmişte kullandığımdan çok daha iyi ve daha kompakt.

Bu çözüm:

  • Bir veritabanı sürücüsüne veya 3. taraf kitaplığına bağlı değildir.
  • Yinelenen sütun adlarında başarısız olmaz
  • Verilerde virgül kullanır
  • Yalnızca virgül değil, herhangi bir sınırlayıcıyı işler (varsayılan olmasına rağmen)

İşte ben geldim:

  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

Bir dize listesine benzersiz numaralar nasıl eklenir? BölümündeUnique cevabım olarak bulunacak yinelenen sütun adlarını işlemek için bir uzantı yöntemine ( ) bağlıdır.

Ve işte BlankToNothingyardımcı fonksiyon:

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

2

Açık kaynaklı bir kütüphane olan Cinchoo ETL ile , CSV dosyasını birkaç kod satırı ile kolayca DataTable'a dönüştürebilirsiniz.

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

Daha fazla bilgi için lütfen codeproject adresini ziyaret edin makalesini .

Umarım yardımcı olur.


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

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


Sürüm 13'te ad alanı çakışmalarını önlemekConfiguration için yeniden adlandırıldığını unutmayın CsvConfiguration . Bu cevap çalışma demosu
dbc

2

ExcelDataReader adlı bir kitaplık kullanıyorum, NuGet'te bulabilirsiniz. Hem ExcelDataReader hem de ExcelDataReader.DataSet uzantısını yüklediğinizden emin olun (ikincisi aşağıda belirtilen gerekli AsDataSet yöntemini sağlar).

Her şeyi tek bir fonksiyonda kapsülledim, doğrudan kodunuza kopyalayabilirsiniz. CSV dosyasına bir yol verin, size tek tablolu bir veri kümesi sağlar.

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();
   }
}

2020 ve buradaki bazı eski cevaplara kıyasla harika bir çözüm. Güzel bir şekilde paketlenmiş ve NuGet'ten popüler ve hafif bir kütüphane kullanıyor. Ve esnektir - CSV'niz hafızadaysa, bunu MemoryStreambir dosya yolu yerine iletmeniz yeterlidir . OP'nin istediği DataTable, DataSet'ten şu şekilde kolayca çıkarılır:result.Tables[0]
Tawab Wakil

1

Sadece bu uzantı yöntemlerini paylaşmak, umarım birisine yardımcı olabilir.

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

0

Bunu kullanın, bir işlev virgül ve alıntıdaki tüm sorunları çözer:

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

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

0

Son zamanlarda şu anda bir nuget paketi olarak mevcut en hızlı olduğunu iddia ediyorum .NET için bir CSV ayrıştırıcı yazdım: Sylvan.Data.Csv .

A yüklemek için bu kütüphaneyi kullanmak DataTableson derece kolaydır.

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

Dosyanızın standart, virgülle ayrılmış başlıklara sahip dosyaları olduğunu varsayarsak, ihtiyacınız olan tek şey budur. Üstbilgisiz dosyaları okumaya ve alternatif sınırlayıcılar vb. Kullanmaya izin verme seçenekleri de vardır.

Ayrıca, sütunların stringdeğerlerden başka bir şey olarak ele alınabilmesi için CSV dosyası için özel bir şema sağlamak da mümkündür . Bu, DataTablesütunlara, çalışmak için daha kolay olabilecek değerlerle yüklenmesine izin verir , çünkü bunlara erişirken onları zorlamanız gerekmez.

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

TypedCsvSchemaICsvSchemaProvidersütunların türlerini tanımlamak için basit bir yol sağlayan bir uygulamadır . Bununla birlikte, ICsvSchemaProviderbenzersizlik veya kısıtlanmış sütun boyutu gibi daha fazla meta veri sağlamak istediğinizde bir özel sağlamak da mümkündür .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.