使用C#访问Excel电子表格有时会返回某些单元格的空白值

我需要访问Excel电子表格,并将电子表格中的数据插入到SQL数据库中。 然而,主键是混合的,大多数是数字,一些是字母数字。

我遇到的问题是,当数字键和字母数字键在同一电子表格中时,字母数字单元格返回空白值,而所有其他单元格都没有问题返回其数据。

我正在使用OleDb方法来访问Excel文件。 用命令string检索数据后,我把数据放入一个DataAdapter,然后填充一个DataSet。 我遍历DataSet中第一个DataTable中的所有行(dr)。

我使用dr [“…”]来引用列。ToString()

如果我在Visual Studio 2008中debugging项目,并查看“扩展属性”,通过将鼠标放在“dr”上,我可以查看DataRow的值,但是应该是字母数字的主键是{}。 其他值用引号括起来,但空白值有大括号。

这是一个C#问题或Excel问题?

有没有人曾经遇到过这个问题,或者可能find解决方法/修复?

提前致谢。

解:

连接string:

Provider = Microsoft.Jet.OLEDB.4.0; Data Source = FilePath; Extended Properties =“Excel 8.0; HDR = Yes; IMEX = 1”;

  1. HDR=Yes; 表示第一行包含列名,而不是数据。 HDR=No; 表明相反。

  2. IMEX=1; 告诉驱动程序总是读取“混合”(数字,date,string等)数据列作为文本。 请注意,这个选项可能会影响Excel表单写访问的负面影响。

SQL语法SELECT * FROM [sheet1$] 。 即Excel工作表名称,后跟一个$并用[ ]括起来。

重要:

  • 查看位于registryREG_DWORD“TypeGuessRows”的[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Jet \ 4.0 \ Engines \ Excel]。 这是不让Excel使用前8行来猜测列数据types的关键。 将此值设置为0以扫描所有行。 这可能会损害性能。

  • 如果Excel工作簿受密码保护,则即使通过连接string提供正确的密码,也无法打开该工作簿进行数据访问。 如果您尝试,您收到以下错误信息:“无法解密文件”。

Excel数据源将为整个列select一个列types。 如果其中一个单元格完全不符合该types,则会留下这样的空白。 我们遇到了一些问题,我们的打字员在数字列中input了“8”(数字前的空格,Excel将其转换为该单元格的string)。 这对我来说是合理的,它会尝试.Netparsing方法,因为它们更健壮,但我想这不是Excel驱动程序的工作原理。

我们的修复,因为我们正在使用数据库导入服务,是logging所有这些“失败”的行。 然后,我们返回到XLS文档并重新键入这些单元格,以确保基础types是正确的。 (我们发现刚刚删除的空间并没有解决它 – 我们必须首先清除整个单元格,而不是重新键入'8')。感觉hacky并不是精明的,但这是我们find的最好的方法。 如果Excel驱动程序本身无法正确读取它,那么一旦进入.Net,就无法将这些数据从那里获取。

另一种情况是Office以简单的名义隐藏了用户的重要细节,因此当你必须准确地用于电力使用时,这会变得更加困难。

{}意味着这是某种空的对象,而不是一个string。 当您将鼠标hover在对象上时,您应该可以看到它的types。 同样,当您使用quickwatch查看dr [“…”]时,您应该看到对象types。 你收到什么types的对象?

ItemArray是一个对象数组。 所以我假设DataRow中的“列”,我试图引用,是types的对象。

对于VISTA兼容性,您可以在连接string中使用EXCEL 12.0驱动程序。 这应该可以解决你的问题。 它是我的。

解:

  1. 你把HDR = No,这样第一行不被视为列标题。 连接string:Provider = Microsoft.Jet.OLEDB.4.0; Data Source = FilePath;扩展属性=“Excel 8.0; HDR =否; IMEX = 1”;
  2. 您忽略第一行,并通过任何您想要的方式访问数据(DataTable,DataReader等)。 您可以通过数字索引来访问列,而不是列名。

它为我工作。 这样你就不必修改寄存器了!

我在这里回答了类似的问题。 在这里,我已经复制并粘贴了相同的答案,以方便您:

我有这个相同的问题,但能够解决它,而不诉诸于Excel COM接口或第三方软件。 它涉及一点处理开销,但似乎为我工作。

  1. 首先读取数据以获取列名称
  2. 然后用这些列中的每一列创build一个新的DataSet,将它们的每个DataType设置为string。
  3. 再次将数据读入这个新的数据集。 瞧 – 科学记数法现在已经消失了,所有的东西都以string的forms被读入。

这里有一些代码说明了这一点,作为一个额外的好处,它甚至StyleCopped!

 public void ImportSpreadsheet(string path) { string extendedProperties = "Excel 12.0;HDR=YES;IMEX=1"; string connectionString = string.Format( CultureInfo.CurrentCulture, "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"{1}\"", path, extendedProperties); using (OleDbConnection connection = new OleDbConnection(connectionString)) { using (OleDbCommand command = connection.CreateCommand()) { command.CommandText = "SELECT * FROM [Worksheet1$]"; connection.Open(); using (OleDbDataAdapter adapter = new OleDbDataAdapter(command)) using (DataSet columnDataSet = new DataSet()) using (DataSet dataSet = new DataSet()) { columnDataSet.Locale = CultureInfo.CurrentCulture; adapter.Fill(columnDataSet); if (columnDataSet.Tables.Count == 1) { var worksheet = columnDataSet.Tables[0]; // Now that we have a valid worksheet read in, with column names, we can create a // new DataSet with a table that has preset columns that are all of type string. // This fixes a problem where the OLEDB provider is trying to guess the data types // of the cells and strange data appears, such as scientific notation on some cells. dataSet.Tables.Add("WorksheetData"); DataTable tempTable = dataSet.Tables[0]; foreach (DataColumn column in worksheet.Columns) { tempTable.Columns.Add(column.ColumnName, typeof(string)); } adapter.Fill(dataSet, "WorksheetData"); if (dataSet.Tables.Count == 1) { worksheet = dataSet.Tables[0]; foreach (var row in worksheet.Rows) { // TODO: Consume some data. } } } } } } } 

在xls文件中按降序排列logging,使字母数字字段出现在标题行下方的顶部。 这确保了第一行数据读取将数据types定义为“varchar”或“nvarchar”

嗨所有这些代码也获得字母数字值

 using System.Data.OleDb; string ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + filepath + ";" + "Extended Properties="+(char)34+"Excel 8.0;IMEX=1;"+(char)34; string CommandText = "select * from [Sheet1$]"; OleDbConnection myConnection = new OleDbConnection(ConnectionString); myConnection.Open(); OleDbDataAdapter myAdapter = new OleDbDataAdapter(CommandText, myConnection); ds = null; ds = new DataSet(); myAdapter.Fill(ds); 

这不完全正确! 显然,如果前8行是空白的,则不pipeIMEX = 1,Jet / ACE总是假设一个stringtypes。 即使我在registry中将行读为0,我仍然有同样的问题。 这是让它正常工作的唯一可靠的方法:

 try { Console.Write(wsReader.GetDouble(j).ToString()); } catch //Lame unfixable bug { Console.Write(wsReader.GetString(j)); }