【问题标题】:Importing Excel into a DataTable Quickly快速将 Excel 导入数据表
【发布时间】:2013-07-10 17:32:12
【问题描述】:

我正在尝试将 Excel 文件读入 Data.DataTable 列表,尽管使用我当前的方法可能需要很长时间。我基本上是逐个工作表,逐个单元格地去工作,而且往往需要很长时间。有没有更快的方法来做到这一点?这是我的代码:

    List<DataTable> List = new List<DataTable>();

    // Counting sheets
    for (int count = 1; count < WB.Worksheets.Count; ++count)
    {
        // Create a new DataTable for every Worksheet
        DATA.DataTable DT = new DataTable();

        WS = (EXCEL.Worksheet)WB.Worksheets.get_Item(count);

        textBox1.Text = count.ToString();

        // Get range of the worksheet
        Range = WS.UsedRange;


        // Create new Column in DataTable
        for (cCnt = 1; cCnt <= Range.Columns.Count; cCnt++)
        {
            textBox3.Text = cCnt.ToString();


                Column = new DataColumn();
                Column.DataType = System.Type.GetType("System.String");
                Column.ColumnName = cCnt.ToString();
                DT.Columns.Add(Column);

            // Create row for Data Table
            for (rCnt = 0; rCnt <= Range.Rows.Count; rCnt++)
            {
                textBox2.Text = rCnt.ToString();

                try
                {
                    cellVal = (string)(Range.Cells[rCnt, cCnt] as EXCEL.Range).Value2;
                }
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
                {
                    ConvertVal = (double)(Range.Cells[rCnt, cCnt] as EXCEL.Range).Value2;
                    cellVal = ConvertVal.ToString();
                }

                // Add to the DataTable
                if (cCnt == 1)
                {

                    Row = DT.NewRow();
                    Row[cCnt.ToString()] = cellVal;
                    DT.Rows.Add(Row);
                }
                else
                {

                    Row = DT.Rows[rCnt];
                    Row[cCnt.ToString()] = cellVal;

                }
            }
        }
        // Add DT to the list. Then go to the next sheet in the Excel Workbook
        List.Add(DT);
    }

【问题讨论】:

  • " 有更快的方法吗?很遗憾没有。"不对。此代码正在为它读取的每个 Excel 单元格值创建(并且错误地未能处理)一个 COM 对象。这是最慢的方法!一次将整个 Worksheet 读入一个数组,然后遍历该数组中的项目,速度要快得多。

标签: c# .net excel office-interop


【解决方案1】:

校准.Value2 是一项昂贵的操作,因为它是一个COM 互操作调用。相反,我会将整个范围读入一个数组,然后循环遍历该数组:

object[,] data = Range.Value2;

// Create new Column in DataTable
for (int cCnt = 1; cCnt <= Range.Columns.Count; cCnt++)
{
    textBox3.Text = cCnt.ToString();

    var Column = new DataColumn();
    Column.DataType = System.Type.GetType("System.String");
    Column.ColumnName = cCnt.ToString();
    DT.Columns.Add(Column);

    // Create row for Data Table
    for (int rCnt = 1; rCnt <= Range.Rows.Count; rCnt++)
    {
        textBox2.Text = rCnt.ToString();

        string CellVal = String.Empty;
        try
        {
            cellVal = (string)(data[rCnt, cCnt]);
        }
        catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
        {
            ConvertVal = (double)(data[rCnt, cCnt]);
            cellVal = ConvertVal.ToString();
        }

        DataRow Row;

        // Add to the DataTable
        if (cCnt == 1)
        {

            Row = DT.NewRow();
            Row[cCnt.ToString()] = cellVal;
            DT.Rows.Add(Row);
        }
        else
        {

            Row = DT.Rows[rCnt + 1];
            Row[cCnt.ToString()] = cellVal;

        }
    }
} 

【讨论】:

  • 这仍然可以完美运行。我有 40k 条记录,处理时间从大约 2 分钟减少到大约 2 秒。
  • 我对答案中的变量用法感到非常困惑。它似乎对菜鸟不友好。 1.我无法在那个地方使用Range.Value2,它显示错误为“无法将对象[]隐式转换为对象[]”。 2. 我不确定Convertval 变量。
  • @parkourkarthik 我现在无法验证,但如果您的范围是单行或单列,您可能会得到一维 object[] 返回,尽管我认为它始终是二维数组.如果您还没有,请随时将其作为一个单独的问题提出。
  • @parkourkarthik ConvertVal 来自 OP 的原始问题,所以我不能说它的目的是什么。不过,它似乎与原始问题无关。
  • 我似乎找不到所有使用的变量的所有声明。
【解决方案2】:

如果其他人正在使用 EPPlus。这个实现非常幼稚,但是有一些 cmets 引起了人们的注意。如果您要在顶部再添加一个方法GetWorkbookAsDataSet(),它将满足 OP 的要求。

    /// <summary>
    /// Assumption: Worksheet is in table format with no weird padding or blank column headers.
    /// 
    /// Assertion: Duplicate column names will be aliased by appending a sequence number (eg. Column, Column1, Column2)
    /// </summary>
    /// <param name="worksheet"></param>
    /// <returns></returns>
    public static DataTable GetWorksheetAsDataTable(ExcelWorksheet worksheet)
    {
        var dt = new DataTable(worksheet.Name);
        dt.Columns.AddRange(GetDataColumns(worksheet).ToArray());
        var headerOffset = 1; //have to skip header row
        var width = dt.Columns.Count;
        var depth = GetTableDepth(worksheet, headerOffset);
        for (var i = 1; i <= depth; i++)
        {
            var row = dt.NewRow();
            for (var j = 1; j <= width; j++)
            {
                var currentValue = worksheet.Cells[i + headerOffset, j].Value;

                //have to decrement b/c excel is 1 based and datatable is 0 based.
                row[j - 1] = currentValue == null ? null : currentValue.ToString();
            }

            dt.Rows.Add(row);
        }

        return dt;
    }

    /// <summary>
    /// Assumption: There are no null or empty cells in the first column
    /// </summary>
    /// <param name="worksheet"></param>
    /// <returns></returns>
    private static int GetTableDepth(ExcelWorksheet worksheet, int headerOffset)
    {
        var i = 1;
        var j = 1;
        var cellValue = worksheet.Cells[i + headerOffset, j].Value;
        while (cellValue != null)
        {
            i++;
            cellValue = worksheet.Cells[i + headerOffset, j].Value;
        }

        return i - 1; //subtract one because we're going from rownumber (1 based) to depth (0 based)
    }

    private static IEnumerable<DataColumn> GetDataColumns(ExcelWorksheet worksheet)
    {
        return GatherColumnNames(worksheet).Select(x => new DataColumn(x));
    }

    private static IEnumerable<string> GatherColumnNames(ExcelWorksheet worksheet)
    {
        var columns = new List<string>();

        var i = 1;
        var j = 1;
        var columnName = worksheet.Cells[i, j].Value;
        while (columnName != null)
        {
            columns.Add(GetUniqueColumnName(columns, columnName.ToString()));
            j++;
            columnName = worksheet.Cells[i, j].Value;
        }

        return columns;
    }

    private static string GetUniqueColumnName(IEnumerable<string> columnNames, string columnName)
    {
        var colName = columnName;
        var i = 1;
        while (columnNames.Contains(colName))
        {
            colName = columnName + i.ToString();
            i++;
        }

        return colName;
    }

【讨论】:

    【解决方案3】:
    【解决方案4】:

    MS Office Interop 很慢,甚至微软也不推荐在服务器端使用 Interop,并且不能用于导入大型 Excel 文件。有关更多详细信息,请参阅 Microsoft 观点的why not to use OLE Automation

    相反,您可以使用任何 Excel 库,例如 EasyXLS。这是一个代码示例,展示了如何读取 Excel 文件:

    ExcelDocument workbook = new ExcelDocument();
    DataSet ds = workbook.easy_ReadXLSActiveSheet_AsDataSet("excel.xls");
    DataTable dataTable = ds.Tables[0];
    

    如果您的 Excel 文件有多个工作表或仅用于导入单元格范围(以获得更好的性能),请查看how to import Excel to DataTable in C# using EasyXLS 上的更多代码示例。

    【讨论】:

      【解决方案5】:
      Dim sSheetName As String
      Dim sConnection As String
      Dim dtTablesList As DataTable
      Dim oleExcelCommand As OleDbCommand
      Dim oleExcelReader As OleDbDataReader
      Dim oleExcelConnection As OleDbConnection
      
      sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Test.xls;Extended Properties=""Excel 12.0;HDR=No;IMEX=1"""
      
      oleExcelConnection = New OleDbConnection(sConnection)
      oleExcelConnection.Open()
      
      dtTablesList = oleExcelConnection.GetSchema("Tables")
      
      If dtTablesList.Rows.Count > 0 Then
          sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString
      End If
      
      dtTablesList.Clear()
      dtTablesList.Dispose()
      
      If sSheetName <> "" Then
      
          oleExcelCommand = oleExcelConnection.CreateCommand()
          oleExcelCommand.CommandText = "Select * From [" & sSheetName & "]"
          oleExcelCommand.CommandType = CommandType.Text
      
          oleExcelReader = oleExcelCommand.ExecuteReader
      
          nOutputRow = 0
      
          While oleExcelReader.Read
      
          End While
      
          oleExcelReader.Close()
      
      End If
      
      oleExcelConnection.Close()
      

      【讨论】:

        【解决方案6】:

        我知道这是一个老话题,但是MiniExcel 库有一个简单快捷的方法,就像下面的代码

            var filePath = @".....demo.xlsx";
            
            List<DataTable> list = new List<DataTable>();
            var sheets = MiniExcel.GetSheetNames(filePath);
            foreach (var sheetName in MiniExcel.GetSheetNames(filePath))
                list.Add(MiniExcel.QueryAsDataTable(filePath,true,sheetName:sheetName));
        

        最好的问候

        【讨论】:

          【解决方案7】:
           class DataReader
              {
                  Excel.Application xlApp;
                  Excel.Workbook xlBook;
                  Excel.Range xlRange;
                  Excel.Worksheet xlSheet;
                  public DataTable GetSheetDataAsDataTable(String filePath, String sheetName)
                  {
                      DataTable dt = new DataTable();
                      try
                      {
                          xlApp = new Excel.Application();
                          xlBook = xlApp.Workbooks.Open(filePath);
                          xlSheet = xlBook.Worksheets[sheetName];
                          xlRange = xlSheet.UsedRange;
                          DataRow row=null;
                          for (int i = 1; i <= xlRange.Rows.Count; i++)
                          {
                              if (i != 1)
                                  row = dt.NewRow();
                              for (int j = 1; j <= xlRange.Columns.Count; j++)
                              {
                                  if (i == 1)
                                      dt.Columns.Add(xlRange.Cells[1, j].value);
                                  else
                                      row[j-1] = xlRange.Cells[i, j].value;
                              }
                              if(row !=null)
                                  dt.Rows.Add(row);
                          }
                      }
                      catch (Exception ex)
                      {
                          Console.WriteLine(ex.Message);
                      }
                      finally
                      {
                          xlBook.Close();
                          xlApp.Quit();
                      }
                      return dt;
                  }
              }
          

          【讨论】:

            猜你喜欢
            • 2023-03-04
            • 1970-01-01
            • 2016-04-21
            • 2021-05-06
            • 2019-10-04
            • 2012-03-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多