Comment lire un fichier CSV dans a.NET données

comment charger un fichier CSV dans un System.Data.DataTable , créant le datatable basé sur le fichier CSV?

l'ordinaire ADO.net la fonctionnalité de permettre cela?

140
demandé sur Aurora0001 2009-06-26 20:49:28

17 réponses

Voici une excellente classe qui va copier des données CSV dans un datatable en utilisant la structure des données pour créer le DataTable:

Un portable et efficace générique analyseur syntaxique pour les fichiers plats

Il est facile à configurer et facile à utiliser. Je vous invite à prendre un coup d'oeil.

76
répondu Jay Riggs 2009-06-26 16:57:43

j'ai utilisé le fournisseur OleDb . Cependant, il a des problèmes si vous lisez dans les lignes qui ont des valeurs numériques, mais vous voulez qu'ils soient traités comme du texte. Cependant, vous pouvez contourner ce problème en créant un fichier schema.ini . Voici ma méthode que j'ai utilisée:

// 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;
    }
}
86
répondu Jim Scott 2017-11-09 02:13:46

j'ai décidé d'utiliser CSV Reader de Sebastien Lorion .

la suggestion de Jay Riggs est également une excellente solution, mais je n'ai pas besoin de toutes les fonctionnalités que Andrew Rissing Générique Parser fournit.

mise à jour 25/10/2010

après avoir utilisé le lecteur Csv de Sebastien Lorion dans mon projet pendant près d'un an et demi, j'ai constaté qu'il jette des exceptions lors de l'analyse de certains fichiers csv que je pense être bien formé.

donc, je suis passé à L'Analyseur Générique D'Andrew Rissing et il semble faire beaucoup mieux.

mise à jour 9/22/2014

de nos jours, j'utilise surtout cette méthode d'extension pour lire le texte délimité:

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

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

mise à jour 20/02/2015

exemple:

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());
}
35
répondu Ronnie Overby 2018-06-08 08:39:11

Hé son fonctionnement 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;
   }

CSV Image enter image description here

tableau de Données Importées enter image description here

22
répondu Shivam Srivastava 2014-12-30 12:27:31

on utilisait toujours le Jet.Pilote OLEDB, jusqu'à ce que nous commencions à aller aux applications 64 bits. Microsoft n'a pas et ne veut pas libérer de pilote de Jet 64 bits. Voici une solution simple que nous avons trouvé qui utilise le fichier.ReadAllLines et de la Corde.Split pour lire et analyser le fichier CSV et charger manuellement un DataTable. Comme indiqué ci-dessus, il ne traite pas la situation où l'une des valeurs de la colonne contient une virgule. Nous l'utilisons principalement pour lire des fichiers de configuration personnalisés - la partie agréable à propos de l'utilisation Les fichiers CSV est que nous pouvons les éditer dans 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);
}
13
répondu Chuck Bevitt 2010-08-17 22:15:43

c'est le code que je l'utilise mais vos applications doivent fonctionner avec net version 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();
        }
8
répondu Thomas 2013-02-06 11:45:02
public DataTable CsvFileToDatatable(string path, bool IsFirstRowHeader)//here Path is root of file and IsFirstRowHeader is header is there or not
        {
            string header = "No";
            string sql = string.Empty;
            DataTable dataTable = null;
            string pathOnly = string.Empty;
            string fileName = string.Empty;

            try
            {

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

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

                if (IsFirstRowHeader)
                {
                    header = "Yes";
                }

                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 = new DataTable();
                            dataTable.Locale = CultureInfo.CurrentCulture;
                            adapter.Fill(dataTable);

                        }
                    }
                }
            }
            finally
            {

            }

            return dataTable;

        }
6
répondu DJ. 2012-03-22 09:39:33

j'ai trouvé ce morceau de code qui utilise Linq et regex pour analyser un fichier CSV. L'article de référence est maintenant plus d'un an et demi vieux, mais n'ont pas rencontré une meilleure façon de parser un CSV en utilisant Linq (et regex) que cela. La mise en garde est la regex appliquée ici est pour les fichiers délimités par des virgules (détectera les virgules à l'intérieur des guillemets!) et qu'il peut ne pas prendre bien aux en-têtes, mais il y a un moyen de surmonter ceux-ci). Prenez un pic:

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
4
répondu Nepa 2011-09-02 18:24:28

vous pouvez y parvenir en utilisant Microsoft.VisualBasic.FileIO.TextFieldParser dll in 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;
        }
4
répondu kombsh 2013-12-11 16:41:06

la meilleure option que j'ai trouvé, et il résout les problèmes où vous pouvez avoir différentes versions de bureau installé, et aussi les problèmes 32/64 bits comme Chuck Bevitt mentionné , est FileHelpers .

il peut être ajouté à vos références de projet en utilisant NuGet et il fournit une solution monocouche:

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);
3
répondu Neo 2017-05-23 11:55:01

pour ceux qui ne veulent pas utiliser une bibliothèque externe et préfèrent ne pas utiliser OleDB, voir l'exemple ci-dessous. Tout ce que j'ai trouvé était soit de L'OleDB, une bibliothèque externe, ou tout simplement se séparer sur la base d'une virgule! Pour mon cas OleDB ne fonctionnait pas donc je voulais quelque chose de différent.

j'ai trouvé un article de MarkJ qui faisait référence à Microsoft.VisualBasic.FileIO.TextFieldParser méthode comme vu ici . L'article est écrit en VB et ne revient pas un datable, donc voir mon exemple ci-dessous.

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
répondu Smeiff 2017-05-23 11:47:31

réponse très simple: si vous n'avez pas un csv complexe qui peut utiliser une fonction de division simple, cela fonctionnera bien pour l'importation (notez cette importation comme chaînes, je fais des conversions de type de données plus tard si je dois)

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

ma méthode si j'importe une table avec un séparateur de chaîne [] et gère le problème où la ligne courante que je lis peut être allé à la ligne suivante dans le fichier csv ou texte <- dans ce cas, je veux boucler jusqu'à ce que j'obtienne le total nombre de lignes dans la première ligne (colonnes)

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;
    }
3
répondu Matt Farguson 2015-05-13 20:02:28

modifié de Mr ChuckBevitt

solution de travail:

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);
}
3
répondu Balaji Selvarajan 2017-05-15 17:43:07

Voici une solution qui utilise ADO.Net 's ODBC text driver:

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

une fois rempli, vous pouvez évaluer les propriétés de la datable, comme ColumnName, pour faire utiliser toutes les puissances de la ADO.Net objets de données.

dans VS2008 vous pouvez utiliser Linq pour obtenir le même effet.

NOTE: Ceci peut être un duplicata de cette ainsi question.

2
répondu Bob Mc 2017-05-23 12:34:44
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;
    }
}
2
répondu Nodir 2011-06-03 15:09:55

ne peut pas résister à ajouter mon propre spin à cela. C'est tellement mieux et plus compact que ce que j'ai utilisé dans le passé.

Cette solution:

  • ne dépend pas d'un pilote de base de données ou d'une bibliothèque tierce partie.
  • ne manquera pas sur les noms de colonne dupliqués
  • Poignées des virgules dans les données
  • gère n'importe quel délimiteur, pas seulement les virgules (bien que ce soit la valeur par défaut))

voilà ce que j'ai trouvé:

  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

cela dépend d'une méthode d'extension ( Unique ) pour gérer les noms de colonnes en double à trouver comme ma réponse dans comment ajouter des nombres uniques à une liste de chaînes

et voici la fonction d'aide BlankToNothing :

  Public Function BlankToNothing(ByVal Value As String) As Object 
    If String.IsNullOrEmpty(Value) Then Return Nothing
    Return Value
  End Function
2
répondu toddmo 2017-05-23 12:02:46

avec Cinchoo ETL - une bibliothèque open source, vous pouvez facilement convertir le fichier CSV à datable avec quelques lignes de code.

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

pour de plus amples informations, veuillez consulter l'article de codeprojet.

J'espère que ça aidera.

0
répondu RajN 2018-07-01 16:51:16