【问题标题】:Using ExcelDataReader to read Excel data starting from a particular cell使用 ExcelDataReader 从特定单元格开始读取 Excel 数据
【发布时间】:2014-12-24 09:21:35
【问题描述】:

我正在使用 ExcelDataReader 从 C# 中的 Excel 工作簿中读取数据。
但是我的 Excel 工作表的结构是这样的,要读取的数据可以从任何特定的单元格开始,而不一定是A1

任何人都可以就如何使用ExcelDataReader 来实现这一点提出建议吗?

【问题讨论】:

    标签: c# excel exceldatareader


    【解决方案1】:

    如果你使用ExcelDataReader 3+你会发现你的reader对象没有AsDataSet()的任何方法,你还需要为ExcelDataReader.DataSet安装另一个包,然后你可以使用AsDataSet()方法.
    此外,IsFirstRowAsColumnNames 没有属性,您需要将其设置在 ExcelDataSetConfiguration 内。

    例子:

    using (var stream = File.Open(originalFileName, FileMode.Open, FileAccess.Read))
    {
        IExcelDataReader reader;
    
        // Create Reader - old until 3.4+
        ////var file = new FileInfo(originalFileName);
        ////if (file.Extension.Equals(".xls"))
        ////    reader = ExcelDataReader.ExcelReaderFactory.CreateBinaryReader(stream);
        ////else if (file.Extension.Equals(".xlsx"))
        ////    reader = ExcelDataReader.ExcelReaderFactory.CreateOpenXmlReader(stream);
        ////else
        ////    throw new Exception("Invalid FileName");
        // Or in 3.4+ you can only call this:
        reader = ExcelDataReader.ExcelReaderFactory.CreateReader(stream)
    
        //// reader.IsFirstRowAsColumnNames
        var conf = new ExcelDataSetConfiguration
        {
            ConfigureDataTable = _ => new ExcelDataTableConfiguration
            {
                UseHeaderRow = true 
            }
        };
    
        var dataSet = reader.AsDataSet(conf);
    
        // Now you can get data from each sheet by its index or its "name"
        var dataTable = dataSet.Tables[0];
    
        //...
    }
    

    您可以像这样找到单元格引用的行号和列号:

    var cellStr = "AB2"; // var cellStr = "A1";
    var match = Regex.Match(cellStr, @"(?<col>[A-Z]+)(?<row>\d+)");
    var colStr = match.Groups["col"].ToString();
    var col = colStr.Select((t, i) => (colStr[i] - 64) * Math.Pow(26, colStr.Length - i - 1)).Sum();
    var row = int.Parse(match.Groups["row"].ToString());
    

    现在您可以使用一些循环从该单元格中读取数据,如下所示:

    for (var i = row; i < dataTable.Rows.Count; i++)
    {
        for (var j = col; j < dataTable.Columns.Count; j++)
        {
            var data = dataTable.Rows[i][j];
        }
    }
    

    更新:

    您可以使用此配置在读取时过滤 Excel 工作表的行和列:

    var i = 0;
    var conf = new ExcelDataSetConfiguration
    {
        UseColumnDataType = true,
        ConfigureDataTable = _ => new ExcelDataTableConfiguration
        {
            FilterRow = rowReader => fromRow <= ++i - 1,
            FilterColumn = (rowReader, colIndex) => fromCol <= colIndex,
            UseHeaderRow = true
        }
    };
    

    【讨论】:

    • 请注意,在新版本 (v3.4) 中不需要验证文件扩展名,因为 ExcelDataReader 为我们验证。如果文件扩展名无效,则会产生异常。 Reference
    • @LuisEduardox tnx,我更新了我的答案以显示您的评论;)。
    • 非常感谢这个伟大的功能。我有一个 XLSX 格式的日期 10/08/2018 出于某种原因,当我将其转换为 CSV 格式时,它也在插入时间 10/08/2018 12:00:00AM 我试图通过设置 UseColumnDataType = false 来攻击它但是,这产生了相同的结果
    • 希望举个例子
    • @l--''''''---------''''''''''''' 我找到了你的问题并添加了一个例子作为答案, HTH ;).
    【解决方案2】:

    为了更清楚,我将从头开始。

    我将依赖https://github.com/ExcelDataReader/ExcelDataReader 中的示例代码,但会进行一些修改以避免不便。

    以下代码检测文件格式,xls 或 xlsx。

    FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read);
    IExcelDataReader excelReader;
    
    //1. Reading Excel file
    if (Path.GetExtension(filePath).ToUpper() == ".XLS")
    {
        //1.1 Reading from a binary Excel file ('97-2003 format; *.xls)
        excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
    }
    else
    {
        //1.2 Reading from a OpenXml Excel file (2007 format; *.xlsx)
        excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
    }
    
    //2. DataSet - The result of each spreadsheet will be created in the result.Tables
    DataSet result = excelReader.AsDataSet();
    
    //3. DataSet - Create column names from first row
    excelReader.IsFirstRowAsColumnNames = false;
    

    现在我们可以更方便地访问文件内容了。我为此使用 DataTable。以下是访问特定单元格并在控制台中打印其值的示例:

    DataTable dt = result.Tables[0];
    Console.WriteLine(dt.Rows[rowPosition][columnPosition]);
    

    如果你不想做一个DataTable,你可以这样做:

    Console.WriteLine(result.Tables[0].Rows[rowPosition][columnPosition]);
    

    重要的是不要试图超出表格的限制,为此您可以看到如下的行数和列数:

    Console.WriteLine(result.Tables[0].Rows.Count);
    Console.WriteLine(result.Tables[0].Columns.Count);
    

    最后,当你完成后,你应该关闭阅读器并释放资源:

    //5. Free resources (IExcelDataReader is IDisposable)
    excelReader.Close();
    

    希望对你有用。

    (我知道这个问题很老了,但我做出这个贡献是为了增强知识库,因为关于这个库的特定实现的材料很少)。

    【讨论】:

    • 是否可以在HttpRequest 正文而不是文件流上执行此操作?我有一个通过 POST 请求正文进入的有效负载
    • @l--''''''------''''''''''''' 好久没用图书馆了,但我现在阅读了文档,显然它只支持 FileStream。我想到的一个想法是使用 GetResponseStream() 并将您的有效负载存储在一个文件中,然后执行经典过程。
    【解决方案3】:

    适用于 ExcelDataReader v3.6.0 及更高版本。 我有点挣扎以迭代行。所以这里对上面的代码有更多的了解。希望它至少对少数人有所帮助。

    using (var stream = System.IO.File.Open(copyPath, FileMode.Open, FileAccess.Read))
                        {
    
                            IExcelDataReader excelDataReader = ExcelDataReader.ExcelReaderFactory.CreateReader(stream);
    
                            var conf = new ExcelDataSetConfiguration()
                            {
                                ConfigureDataTable = a => new ExcelDataTableConfiguration
                                {
                                    UseHeaderRow = true
                                }
                            };
    
                            DataSet dataSet = excelDataReader.AsDataSet(conf);
                            //DataTable dataTable = dataSet.Tables["Sheet1"];
                            DataRowCollection row = dataSet.Tables["Sheet1"].Rows;
                            //DataColumnCollection col = dataSet.Tables["Sheet1"].Columns;
    
                            List<object> rowDataList = null;
                            List<object> allRowsList = new List<object>();
                            foreach (DataRow item in row)
                            {
                                rowDataList = item.ItemArray.ToList(); //list of each rows
                                allRowsList.Add(rowDataList); //adding the above list of each row to another list
                            }
    
                        }
    

    【讨论】:

      【解决方案4】:

      一种方法:

      FileStream stream = File.Open(@"c:\working\test.xls", FileMode.Open, FileAccess.Read);
      
      IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
      
      excelReader.IsFirstRowAsColumnNames = true;
      
      DataSet result = excelReader.AsDataSet();
      

      result.Tables 包含工作表,result.tables[0].Rows 包含单元格行。

      【讨论】:

      • 您将如何从特定单元格开始读取 Excel?您能否详细解释一下@Sievajet?
      【解决方案5】:

      我发现这对于从特定的列和行中读取很有用:

      FileStream stream = File.Open(@"C:\Users\Desktop\ExcelDataReader.xlsx", FileMode.Open, FileAccess.Read);
      IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
      DataSet result = excelReader.AsDataSet();
      excelReader.IsFirstRowAsColumnNames = true;         
      DataTable dt = result.Tables[0];
      string text = dt.Rows[1][0].ToString();
      

      【讨论】:

        【解决方案6】:

        使用 ExcelReaderFactory 3.1 及更高版本非常容易:

        using (var openFileDialog1 = new OpenFileDialog { Filter = "Excel Workbook|*.xls;*.xlsx;*.xlsm", ValidateNames = true })
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                var fs = File.Open(openFileDialog1.FileName, FileMode.Open, FileAccess.Read);
                var reader = ExcelReaderFactory.CreateBinaryReader(fs);
                var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration
                {
                    ConfigureDataTable = _ => new ExcelDataTableConfiguration
                    {
                        UseHeaderRow = true // Use first row is ColumnName here :D
                    }
                });
                if (dataSet.Tables.Count > 0)
                {
                    var dtData = dataSet.Tables[0];
                    // Do Something
                }
            }
        }
        

        【讨论】:

          【解决方案7】:
          public static DataTable ConvertExcelToDataTable(string filePath, bool isXlsx = false)
          {
              System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
              //open file and returns as Stream
                  using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
                  {
                          using (var reader = ExcelReaderFactory.CreateReader(stream))
                          {
          
                              var conf = new ExcelDataSetConfiguration
                              {
                                  ConfigureDataTable = _ => new ExcelDataTableConfiguration
                                  {
                                      UseHeaderRow = true
                                  }
                              };
          
                              var dataSet = reader.AsDataSet(conf);
          
                              // Now you can get data from each sheet by its index or its "name"
                              var dataTable = dataSet.Tables[0];
          
                              Console.WriteLine("Total no of rows  " + dataTable.Rows.Count);
                              Console.WriteLine("Total no of Columns  " + dataTable.Columns.Count);
          
                              return dataTable;
          
                          }
          
                  }
             
          }
          

          【讨论】:

          • 我不知道这是否正确,但由于这是对一个相当老的问题的新答案以及其他高度投票的答案,因此总结一下您的方法的不同之处会很有用以及为什么要分享它。
          【解决方案8】:

          您可以使用 .NET 库来做同样的事情,我认为这更简单。

          string ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; data source={path of your excel file}; Extended Properties=Excel 12.0;";
          
                  OleDbConnection objConn = null;
                  System.Data.DataTable dt = null;
                  //Create connection object by using the preceding connection string.
                  objConn = new OleDbConnection(connString);
                  objConn.Open();
                  //Get the data table containg the schema guid.
                  dt = objConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
                  string sql = string.Format("select * from [{0}$]", sheetName);
                  var adapter = new System.Data.OleDb.OleDbDataAdapter(sql, ConnectionString);
                  var ds = new System.Data.DataSet();
                  string tableName = sheetName;
                  adapter.Fill(ds, tableName);
                  System.Data.DataTable data = ds.Tables[tableName];
          

          在数据表中获得数据后,您可以像通常使用 DataTable 类一样访问它们。

          【讨论】:

          • 但这一直抛出一个错误,说它不受支持..我试过这个social.msdn.microsoft.com/Forums/en-US/…
          • 问题集中在ExelDataReaer,这是一个用来避免使用OLEDB的库。 ExelDataReaer 将它们作为二进制文件读取。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-05-25
          • 2012-10-26
          • 1970-01-01
          • 1970-01-01
          • 2015-06-04
          • 2015-07-21
          • 2018-04-20
          相关资源
          最近更新 更多