使用ClosedXML将Excel工作表读入DataTable

12

我想将Excel工作表的内容读入C# DataTable中。Excel工作表可能具有可变数量的列和行。 Excel工作表的第一行将始终包含列名称,但其他行可能为空。

我在SO上看到的所有建议都假定存在Microsoft.ACE.OLEDB。由于我尝试其中一些解决方案时出现此错误,因此我没有安装此库。

Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.

我觉得很奇怪,因为我已经安装了Office 2016。

因此,我希望使用Nuget上的ClosedXML库,但是我在他们的维基中没有看到任何关于在C#中将Excel工作表读取为DataTable的示例。

2个回答

23

这个例子不是我写的。我记不得它来自哪里了,因为它在我的档案中。然而,这对我很有效。唯一的问题是空单元格。根据ClosedXML GitHUb wiki页面上的讨论,这与Excel无法跟踪未被数据包围的空单元格有关。我发现,如果我向单元格添加数据,然后删除相同的数据,该过程就会起作用。

public static DataTable ImportExceltoDatatable(string filePath, string sheetName)
{
  // Open the Excel file using ClosedXML.
  // Keep in mind the Excel file cannot be open when trying to read it
  using (XLWorkbook workBook = new XLWorkbook(filePath))
  {
    //Read the first Sheet from Excel file.
    IXLWorksheet workSheet = workBook.Worksheet(1);

    //Create a new DataTable.
    DataTable dt = new DataTable();

    //Loop through the Worksheet rows.
    bool firstRow = true;
    foreach (IXLRow row in workSheet.Rows())
    {
      //Use the first row to add columns to DataTable.
      if (firstRow)
      {
        foreach (IXLCell cell in row.Cells())
        {
          dt.Columns.Add(cell.Value.ToString());
        }
        firstRow = false;
      }
      else
      {
        //Add rows to DataTable.
        dt.Rows.Add();
        int i = 0;

        foreach (IXLCell cell in row.Cells(row.FirstCellUsed().Address.ColumnNumber, row.LastCellUsed().Address.ColumnNumber))
        {
          dt.Rows[dt.Rows.Count - 1][i] = cell.Value.ToString();
          i++;
        }
      }
    }

    return dt;
  }
}

需要添加。
using System.Data;
using ClosedXML.Excel;

除了ClosedXML nuget包之外

对于其他日期时间数据类型...这可能会有所帮助... 参考文献

if (cell.Address.ColumnLetter=="J") // Column with date datatype
 {
    DateTime dtime = DateTime.FromOADate(double.Parse(cell.Value.ToString()));
                     dt.Rows[dt.Rows.Count - 1][i] = dtime;
 }
 else
 {
      dt.Rows[dt.Rows.Count - 1][i] = cell.Value.ToString();
 }

1
在这一行抛出空引用异常。foreach (IXLCell cell in row.Cells(row.FirstCellUsed().Address.ColumnNumber,row.LastCellUsed().Address.ColumnNumber)) - Jyotirmaya Prusty
1
@JyotirmayaPrusty 将包含for循环的else条件的内容包装在条件“if(row.FirstCellUsed() != null)”中。否则,它会在完全空行上失败。 - Brandon Barkley
如果空单元格是一个问题,那么在现实世界中这不会成为一个大问题吗?也就是说,这种方法不是纯理论(理论上好,实践上不好)吗? - user16594857
1
考虑性能:(1)添加行的方式:https://dev59.com/-GMl5IYBdhLWcg3wXF9f#18465931 (2)对于大型表格,整个工作表的行操作代价高昂,而范围更快:6列x 1000000行-> 230秒vs 10秒。也许可以通过3个步骤解决:首先通过第一行获取标题/列;计算范围(列范围和最大行数);获取范围并迭代其行。 - Kiryl
foreach 循环在处理第一列和最后一列信息时出现了问题。更好的做法是根据(1,1,ws.Rows().Count,38)定义一个基于范围的名称,基本上写成 (A1:AL),然后只需通过 .Cells() 迭代即可。当 FirstColumn 为空时,它会将所有数据向左移动,而 Col1 可以有空值。 - KeithL
显示剩余3条评论

20

使用此代码,您可以读取Excel表格的内容。您可以指定工作表的名称或编号,将返回一个数据集,其中包含该工作表的内容。

public static DataTable GetDataFromExcel(string path, dynamic worksheet)
        {
            //Save the uploaded Excel file.


            DataTable dt = new DataTable();
            //Open the Excel file using ClosedXML.
            using (XLWorkbook workBook = new XLWorkbook(path))
            {
                //Read the first Sheet from Excel file.
                IXLWorksheet workSheet = workBook.Worksheet(worksheet);

                //Create a new DataTable.

                //Loop through the Worksheet rows.
                bool firstRow = true;
                foreach (IXLRow row in workSheet.Rows())
                {
                    //Use the first row to add columns to DataTable.
                    if (firstRow)
                    {
                        foreach (IXLCell cell in row.Cells())
                        {
                            if (!string.IsNullOrEmpty(cell.Value.ToString()))
                            {
                                dt.Columns.Add(cell.Value.ToString());
                            }
                            else
                            {
                                break;
                            }
                        }
                        firstRow = false;
                    }
                    else
                    {
                        int i = 0;
                        DataRow toInsert = dt.NewRow();
                        foreach (IXLCell cell in row.Cells(1, dt.Columns.Count))
                        {
                            try
                            {
                                toInsert[i] = cell.Value.ToString();
                            }
                            catch (Exception ex)
                            {

                            }
                            i++;
                        }
                        dt.Rows.Add(toInsert);
                    }
                }
                return dt;
            }

3
这应该是被接受的答案。它按预期工作。 - Jyotirmaya Prusty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接