ADO.NET(OleDb)读取Excel表格时的一个BUG
2015-07-27 09:16
176 查看
假设我们有如下一个Excel表格:
现在要使用C#程序读取其内容:
using System;
using System.Data.OleDb;
namespace Skyiv.Ben.Test
{
sealed class ExcelTest
{
static void Main()
{
try
{
using (OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=\"Excel 8.0;HDR=yes\";Data Source=Std"))
{
conn.Open();
OleDbCommand comm = new OleDbCommand("SELECT [ID],[NAME] FROM [Sheet1$]", conn);
using (OleDbDataReader r = comm.ExecuteReader())
{
while (r.Read())
{
int id = Convert.ToInt32(r.GetValue(0));
string name = Convert.ToString(r.GetValue(1));
Console.WriteLine("{0}:\t{1}", id, name);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("错误: " + ex.Message);
}
}
}
}
该程序的运行结果如下:
看来在读取单元格“B3”时返回了“DBNull”,而不是正确的“1768”。使用我的博客园发表的一篇随笔“数据库小工具(C#)”中给出的 OleDb.exe 查看了一下:
确实,该 Execl 表格中有 2 个“DBNull”值,再用该工具查看一下其结构:
发现其第一列“ID”的数据类型是“double”,第二列“NAME”的数据类型是“string”。经分析,“DBNull”都出现在其数据类型与列的数据类型不相符的单元格中。看来,问题的症结就在这里了。我们知道,Excel 表格并是不真正的数据库,不像真正的数据库一样每个字段(列)都有一个特定的数据类型,而是由ADO.NET通过扫描该表格的开头几行来推测其每一列的数据类型,这样,当某列中有些单元格的数据类型与该列数据类型不一致时,就出问题了,该单元格的值就变成的“DBNull”。
这个问题来源于我的实际工作。在工作中,需要分析一张业务部门提供的 Excel 表格中的数据,该表格有好几千个数据行,其中有些列绝大部分的值是数字型,但其中有一些数字存储为文本格式。而有些列绝大部分的值是字符型,但有少数单元格的值是数字。这样,我的分析程序就不能工作了。我目前的解决方案是将该 Excel 表格另存为文本文件(制表符分隔),然后在 C# 程序中读取该文本文件。
还有一种方法就是在 Excel 表格中选中整列,然后“将存为文本的数字转换为数字”,如下所示:
但是,我并没有找到一个简便的方法来“将数字转换为文本”。
不知在 ADO.NET 中有没有办法在不改变原始 Excel 表格的情况下,正确读取其列中有单元格的数据类型不一致的 Excel 表格中的数据?如果谁知道的话,恳请告诉我。谢谢!
不知道要通过什么途径向 Microsoft 报告这个 BUG ?
我认为这个 BUG 的解决方案有两个:
1. 如果某一列被推测为数字型的话,如果在该列中出现字符型的数据,如果该数据是存储为文本的数字,就直接转换为数字返回给调用者好了。如果该数据不能转换为数字,可以返回“DBNull”,或者抛出异常。
如果某一列被推测为字符型的话,只要该列中的单元格不为空,就转换为字符型返回给调用者。
2. 在 ADO.NET 的 OleDb 连接串中提供一个属性强制指定 Excel 表格中的所有列的数据类型都为“string”,只要单元格的内容不为空,就不返回“DBNull”,而返回“string”。然后由调用者自己使用 Convert.ToXXX() 方法转换到合适的数字类型。
不知大家以为然否?
现在要使用C#程序读取其内容:
using System;
using System.Data.OleDb;
namespace Skyiv.Ben.Test
{
sealed class ExcelTest
{
static void Main()
{
try
{
using (OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=\"Excel 8.0;HDR=yes\";Data Source=Std"))
{
conn.Open();
OleDbCommand comm = new OleDbCommand("SELECT [ID],[NAME] FROM [Sheet1$]", conn);
using (OleDbDataReader r = comm.ExecuteReader())
{
while (r.Read())
{
int id = Convert.ToInt32(r.GetValue(0));
string name = Convert.ToString(r.GetValue(1));
Console.WriteLine("{0}:\t{1}", id, name);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("错误: " + ex.Message);
}
}
}
}
该程序的运行结果如下:
看来在读取单元格“B3”时返回了“DBNull”,而不是正确的“1768”。使用我的博客园发表的一篇随笔“数据库小工具(C#)”中给出的 OleDb.exe 查看了一下:
确实,该 Execl 表格中有 2 个“DBNull”值,再用该工具查看一下其结构:
发现其第一列“ID”的数据类型是“double”,第二列“NAME”的数据类型是“string”。经分析,“DBNull”都出现在其数据类型与列的数据类型不相符的单元格中。看来,问题的症结就在这里了。我们知道,Excel 表格并是不真正的数据库,不像真正的数据库一样每个字段(列)都有一个特定的数据类型,而是由ADO.NET通过扫描该表格的开头几行来推测其每一列的数据类型,这样,当某列中有些单元格的数据类型与该列数据类型不一致时,就出问题了,该单元格的值就变成的“DBNull”。
这个问题来源于我的实际工作。在工作中,需要分析一张业务部门提供的 Excel 表格中的数据,该表格有好几千个数据行,其中有些列绝大部分的值是数字型,但其中有一些数字存储为文本格式。而有些列绝大部分的值是字符型,但有少数单元格的值是数字。这样,我的分析程序就不能工作了。我目前的解决方案是将该 Excel 表格另存为文本文件(制表符分隔),然后在 C# 程序中读取该文本文件。
还有一种方法就是在 Excel 表格中选中整列,然后“将存为文本的数字转换为数字”,如下所示:
但是,我并没有找到一个简便的方法来“将数字转换为文本”。
不知在 ADO.NET 中有没有办法在不改变原始 Excel 表格的情况下,正确读取其列中有单元格的数据类型不一致的 Excel 表格中的数据?如果谁知道的话,恳请告诉我。谢谢!
不知道要通过什么途径向 Microsoft 报告这个 BUG ?
我认为这个 BUG 的解决方案有两个:
1. 如果某一列被推测为数字型的话,如果在该列中出现字符型的数据,如果该数据是存储为文本的数字,就直接转换为数字返回给调用者好了。如果该数据不能转换为数字,可以返回“DBNull”,或者抛出异常。
如果某一列被推测为字符型的话,只要该列中的单元格不为空,就转换为字符型返回给调用者。
2. 在 ADO.NET 的 OleDb 连接串中提供一个属性强制指定 Excel 表格中的所有列的数据类型都为“string”,只要单元格的内容不为空,就不返回“DBNull”,而返回“string”。然后由调用者自己使用 Convert.ToXXX() 方法转换到合适的数字类型。
不知大家以为然否?
相关文章推荐
- hdu 2853Assignment (二分图的最大权匹配)
- #define宏中的“#”和“##”
- 用 grunt-contrib-connect 构建实时预览开发环境
- win7 x64 驱动
- 程序员八荣八耻
- 1、AEC-实用口语寒暄Greetings
- Android Studio项目结构
- 关于Spark hello world环境搭建
- .gitignore文件不起作用
- SAP HANA 高可用性 (High Availability) 解决方案 (三) -系统复制(System Replication)
- ScrollView ViewPager ListView三者共存的问题
- css3基本属性
- SQL SERVER与C#的数据类型对应表
- 好声音,周杰伦
- c文件到可执行文件运行的过程探索--堆栈中的过程活动记录
- Qt同步线程的几种方法
- shell版俄罗斯方块二:界面绘制
- 主键、外键、复合外键的创建
- shell版俄罗斯方块二:界面绘制 分类: shell版俄罗斯方块 2015-07-27 09:14 116人阅读 评论(0) 收藏
- 90个常用词根,30个前缀30个后缀