【问题标题】:How to read a CSV file into a .NET Datatable如何将 CSV 文件读入 .NET 数据表
【发布时间】:2010-11-06 05:06:19
【问题描述】:

如何将 CSV 文件加载到 System.Data.DataTable,基于 CSV 文件创建数据表?

常规的 ADO.net 功能是否允许这样做?

【问题讨论】:

  • 这怎么可能是“离题”?这是一个具体的问题,100 人认为它很有用
  • @Ryan:我真的对你说... StackOverflow 版主是一群毒蛇。支持我,StackOverflow 版主!

标签: c# .net csv datatable


【解决方案1】:

我一直在使用OleDb 提供程序。但是,如果您正在读取具有数值但希望将它们视为文本的行,则会出现问题。但是,您可以通过创建 schema.ini 文件来解决该问题。这是我使用的方法:

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

【讨论】:

  • 谢谢哥们。这对我有帮助。我有一个 CSV 文件,其中逗号不仅是分隔符,而且在许多列值中无处不在,所以想出一个分割行的正则表达式有点挑战性。 OleDbProvider 正确推断了架构。
  • 实现是有道理的,但是我们如何处理包含混合数据类型的单元格。比如40C等?
  • 虽然这个答案可行,但我强烈建议不要这样做。您引入了一个外部依赖项,该依赖项可能与同一台机器上的其他 Office 安装冲突(在本地开发环境中使用 Excel?),取决于安装的版本。有 NuGet 包(ExcelDataReader、CsvHelper)以更高效、更便携的方式执行此操作。
  • @A.Murray - 你到底是什么意思?这使用 System.Data.dll 中的内置 OleDb 提供程序。您不需要安装任何额外的“驱动程序”。如果任何 Windows 安装都没有安装基本的 Jet 驱动程序,我会在这个时代感到震惊。这是 1990 年代的 CSV....
  • +1 我使用的是 64 位目标,因此我必须用“Provider=Microsoft.ACE.OLEDB.12.0;Data”替换提供程序来代替“Microsoft.Jet.OLEDB.4.0” " 并且我在一个循环中添加了所有 csv,其余代码对我有用
【解决方案2】:

这是一个优秀的类,它将使用数据结构将 CSV 数据复制到数据表中以创建 DataTable:

A portable and efficient generic parser for flat files

它易于配置且易于使用。我劝你看看。

【讨论】:

  • 确实很棒。它对我来说非常适合开箱即用,甚至无需阅读文档。
  • 这是否适用于每行可能具有不同结构的 CSV 文件?我有一个日志文件,其中包含不同类型的记录事件,需要将其分成多个表。
  • @gonzobrains - 可能不是; CSV 文件的基本假设是基于第一行中指定的一组列标题的矩形数据结构。您所拥有的似乎是更通用的逗号分隔的可区分数据,需要更复杂的“ETL”才能从文件解析为不同类型的对象实例(可能包括不同 DataTables 的 DataRows)。
  • 哇,这对我来说也是开箱即用的。我尝试了 Microsoft.Office.Interop.Excel、IronXL 和 DocumentFormat.OpenXml,它们的运行速度都非常慢。 3k 行大约需要 10 多秒。这个解析器在我 mouseUp 的时候完成了操作!我使用了 nuget,因为那是最简单的。惊讶!
【解决方案3】:

嘿,它的工作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 图像

数据表已导入

【讨论】:

  • 仅当 100% 的输入是最简单的 CSV 文件时(在您的情况下可能是这样)。
  • 你是对的。你应该使用codeproject.com/Articles/9258/A-Fast-CSV-Reader( Lorion dll ) 我试过它工作得很好。
  • 查看我 2009 年的回答。
  • @ShivamSrivastava 我收到最后一行的错误,你在那儿然后给你其他联系信息
  • 这对我有用,非常感谢 .. 好的部分 .. 没有第三方 dll 的 .. :)
【解决方案4】:

我决定使用Sebastien Lorion's Csv Reader

Jay Riggs 的建议也是一个很好的解决方案,但我只是不需要 Andrew Rissing's Generic Parser 提供的所有功能。

2010 年 10 月 25 日更新

在我的项目中使用Sebastien Lorion's Csv Reader 将近一年半后,我发现它在解析一些我认为格式正确的 csv 文件时会引发异常。

所以,我确实切换到了Andrew Rissing's Generic Parser ,它似乎做得好多了。

2014 年 9 月 22 日更新

这些天,我主要使用这种扩展方法来阅读分隔文本:

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

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

2015 年 2 月 20 日更新

例子:

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 的 CSV 阅读器很棒。我将它用于繁重的 CSV 处理,但我也将 Andrew's Rissing's 用于小型工作,它对我很有帮助。玩得开心!
  • 如何使用这些类将 CSV 加载到 DATATABLE 中?
  • 我试过了,但 it.Current.Keys 集合返回“System.Linq.Enumerable+WhereSelectListIterator`2[System.Int32,System.Char]”而不是列名。有什么想法吗?
  • 可以使用多字符分隔符吗?
  • 不,但我考虑过启用它。
【解决方案5】:

在我们开始使用 64 位应用程序之前,我们一直使用 Jet.OLEDB 驱动程序。 Microsoft 没有也不会发布 64 位 Jet 驱动程序。这是我们提出的一个简单解决方案,它使用 File.ReadAllLines 和 String.Split 来读取和解析 CSV 文件并手动加载 DataTable。如上所述,它不处理其中一个列值包含逗号的情况。我们主要使用它来读取自定义配置文件 - 使用 CSV 文件的好处在于我们可以在 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);
}

【讨论】:

    【解决方案6】:

    您可以通过在 C# 中使用 Microsoft.VisualBasic.FileIO.TextFieldParser dll 来实现它

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

    【讨论】:

    • 请不要尝试用 CSV 处理重新发明轮子。有很多非常强大的开源替代品。
    • 感谢 Brad,关于处理嵌入引号的 TextFieldParser 的有用提示。
    【解决方案7】:

    这是我使用的代码,但您的应用程序必须使用 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();
            }
    

    【讨论】:

    • 这正是我想要呈现的。
    【解决方案8】:

    我发现的最佳选项是FileHelpers,它解决了您可能安装了不同版本的 Office 以及诸如 Chuck Bevitt mentioned 之类的 32/64 位问题。

    可以使用 NuGet 将其添加到您的项目引用中,并提供单线解决方案:

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

    【讨论】:

    • 你能说出什么是CommonEngine吗? NuGet 是否与 NuGet.Core 相同。我在参考文献中只找到了 NuGet.Core
    • 这是您需要的 FileHelpers。如果您有 NuGet,请使用 NuGet 添加它。否则,只需将其作为程序集添加到您的项目中。 CommonEngine 是 FileHelpers 的一部分。
    • 这是迄今为止我遇到的最好和最简单的选择。非常感谢!
    【解决方案9】:

    改编自 ChuckBevitt 先生

    工作解决方案:

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

    【讨论】:

    • 所以这解决了内存问题对吧?这是逐行处理而不是持久存在内存中,所以应该没有例外?我喜欢这种处理方式,但 File.ReadAllLines() 不将所有内容保存到内存中吗?我认为您应该使用 File.ReadLines() 来避免巨大的内存缓冲区?对于我只想了解内存问题的手头问题,这是一个很好的答案。
    【解决方案10】:
        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;
        }
    

    使用:https://joshclose.github.io/CsvHelper/

    【讨论】:

    【解决方案11】:
    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;
        }
    }
    

    【讨论】:

      【解决方案12】:

      我遇到了这段代码,它使用 Linq 和正则表达式来解析 CSV 文件。参考文章现在已经有一年半的历史了,但还没有找到比这更简洁的方法来使用 Linq(和正则表达式)解析 CSV。需要注意的是,此处应用的正则表达式适用于逗号分隔的文件(将检测引号内的逗号!)并且它可能不适用于标题,但有一种方法可以克服这些)。顶一下:

      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
      

      【讨论】:

        【解决方案13】:

        对于那些不想使用外部库并且不想使用 OleDB 的人,请参阅下面的示例。我发现的所有东西要么是 OleDB、外部库,要么只是基于逗号分割!就我而言,OleDB 无法正常工作,所以我想要一些不同的东西。

        我发现 MarkJ 的一篇文章引用了 Microsoft.VisualBasic.FileIO.TextFieldParser 方法,如 here 所示。这篇文章是用VB写的,不返回数据表,请看下面我的例子。

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

        【讨论】:

          【解决方案14】:

          非常基本的答案:如果您没有可以使用简单拆分函数的复杂 csv,这将适用于导入(注意此导入为字符串,如果需要,我稍后会进行数据类型转换)

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

          如果我要导入带有字符串 [] 分隔符的表并处理我正在阅读的当前行可能已转到 csv 或文本文件中的下一行的问题,我的方法

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

          【讨论】:

          • 很好,您还没有将行声明为字符串[]。
          • @AnimalStyle 你是对的 - 更新了更强大的方法和声明的行
          【解决方案15】:

          使用Cinchoo ETL - 一个开源库,您可以通过几行代码轻松地将 CSV 文件转换为 DataTable。

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

          更多信息,请访问codeproject文章。

          小提琴示例: https://dotnetfiddle.net/jxhUVX

          【讨论】:

            【解决方案16】:

            这是一个使用 ADO.Net 的 ODBC 文本驱动程序的解决方案:

            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
            

            填充后,您可以对数据表的属性(如 ColumnName)进行赋值,以充分利用 ADO.Net 数据对象的所有功能。

            在VS2008中你可以使用Linq来达到同样的效果。

            注意:这可能与this SO 问题重复。

            【讨论】:

              【解决方案17】:

              无法抗拒为此添加我自己的旋转。这比我过去使用的要好得多,也更紧凑。

              这个解决方案:

              • 不依赖于数据库驱动程序或第 3 方库。
              • 不会因重复的列名而失败
              • 处理数据中的逗号
              • 处理任何分隔符,而不仅仅是逗号(尽管这是默认设置)

              这是我想出的:

                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
              

              它依赖于扩展方法 (Unique) 来处理重复的列名作为我在 How to append unique numbers to a list of strings 中的答案

              这是BlankToNothing 辅助函数:

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

              【讨论】:

                【解决方案18】:

                我使用了一个名为 ExcelDataReader 的库,你可以在 NuGet 上找到它。请务必同时安装 ExcelDataReader 和 ExcelDataReader.DataSet 扩展(后者提供下面引用的所需 AsDataSet 方法)。

                我将所有内容都封装在一个函数中,您可以直接将其复制到您的代码中。 给它一个 CSV 文件的路径,它会给你一个包含一个表的数据集。

                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 年,与此处的一些较旧答案相比,这是一个很好的解决方案。它被很好地打包并使用来自 NuGet 的流行且轻量级的库。而且它很灵活——如果您的 CSV 在内存中,只需将其作为MemoryStream 而不是文件路径传递。 OP 要求的 DataTable 很容易从 DataSet 中提取出来,如下所示:result.Tables[0]
                【解决方案19】:

                我最近为 .NET 编写了一个 CSV parserI'm claiming is currently the fastest 可作为 nuget 包使用:Sylvan.Data.Csv

                使用这个库来加载DataTable 非常简单。

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

                假设您的文件是带有标题的标准逗号分隔文件,这就是您所需要的。还有一些选项可以允许读取没有标题的文件,以及使用备用分隔符等。

                还可以为 CSV 文件提供自定义架构,以便可以将列视为 string 值以外的其他内容。这将允许 DataTable 列加载更易于使用的值,因为您在访问它们时不必强制它们。

                这可以通过提供一个 ICsvSchemaProvider 实现来实现,它公开了一个方法 DbColumn? GetColumn(string? name, int ordinal)DbColumn 类型是System.Data.Common 中定义的抽象类型,这意味着如果您实现自己的模式提供程序,您也必须提供它的实现。 DbColumn 类型公开有关列的各种元数据,您可以根据需要选择公开尽可能多的元数据。最重要的元数据是DataTypeAllowDBNull

                一个公开类型信息的非常简单的实现可能如下所示:

                class TypedCsvColumn : DbColumn
                {
                    public TypedCsvColumn(Type type, bool allowNull)
                    {
                        // if you assign ColumnName here, it will override whatever is in the csv header
                        this.DataType = type;
                        this.AllowDBNull = allowNull;
                    }
                }
                    
                class TypedCsvSchema : ICsvSchemaProvider
                {
                    List<TypedCsvColumn> columns;
                
                    public TypedCsvSchema()
                    {
                        this.columns = new List<TypedCsvColumn>();
                    }
                
                    public TypedCsvSchema Add(Type type, bool allowNull = false)
                    {
                        this.columns.Add(new TypedCsvColumn(type, allowNull));
                        return this;
                    }
                
                    DbColumn? ICsvSchemaProvider.GetColumn(string? name, int ordinal)
                    {
                        return ordinal < columns.Count ? columns[ordinal] : null;
                    }
                }
                

                要使用此实现,您需要执行以下操作:

                
                var schema = new TypedCsvSchema()
                    .Add(typeof(int))
                    .Add(typeof(string))
                    .Add(typeof(double), true)
                    .Add(typeof(DateTime))
                    .Add(typeof(DateTime), true);
                var options = new CsvDataReaderOptions
                {
                    Schema = schema
                };
                
                
                using var dr = CsvDataReader.Create("data.csv", options);
                ...
                

                【讨论】:

                • 我不能使用你的任何代码,我猜它已经过时了。你能更新你的帖子吗?
                • @speyck 确实,它已经过时了。我在这里更新了样本。如果您有任何疑问或问题,请随时在github.com/MarkPflug/Sylvan 提出问题
                【解决方案20】:

                使用这个,一个函数解决所有逗号和引号问题:

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

                【讨论】:

                  【解决方案21】:

                  只是分享这个扩展方法,希望对大家有所帮助。

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

                  【讨论】:

                    【解决方案22】:
                     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
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-06-24
                      • 2018-07-10
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-06-01
                      • 2015-06-03
                      相关资源
                      最近更新 更多