最近在处理C#读取excel文件时碰到了个BT问题,有部分数据读取失败了,翻看了不少资料,终于找到了问题的所在,所以在这里跟大伙分享下:
简要问题分析:
用C#读取excel文件数据时,出现数据丢失现象。
经查资料,发现产生这种问题的根源与Excel ISAM[3](Indexed Sequential Access Method,即索引顺序存取方法)驱动程序的限制有关,Excel ISAM 驱动程序通过检查前几行中实际值确定一个 Excel 列的类型,然后选择能够代表其样本中大部分值的数据类型[4]。也即Excel ISAM查找某列前几行(默认情况下是8行),把占多的类型作为其处理类型。例如如果数字占多,那么其它含有字母等文本的数据项就会置空;相反如果文本居多,纯数字的数据项就会被置空。
相关知识:
现具体分析HDR和IMEX所代表的含义。
HDR用来设置是否将Excel表中第一行作为字段名,“YES”代表是,“NO”代表不是即也为数据内容;
IMEX是用来告诉驱动程序使用Excel文件的模式,其值有0、1、2三种,分别代表导出、导入、混合模式。当我们设置IMEX=1时将强制混合数据转换为文本,但仅仅这种设置并不可靠,IMEX=1只确保在某列前8行数据至少有一个是文本项的时候才起作用,它只是把查找前8行数据中数据类型占优选择的行为作了略微的改变。例如某列前8行数据全为纯数字,那么它仍然以数字类型作为该列的数据类型,随后行里的含有文本的数据仍然变空。
网络上大部分的解决方法:
1、修改连接字符串:
如原来的连接字符串为: string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Path + "; Extended Properties=Excel 8.0;";
将其修改为:string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Path + ";Extended Properties=Excel 8.0;IMEX=1;";
2、我还看到的一些改进的措施是IMEX=1与注册表值TypeGuessRows配合使用。
TypeGuessRows 值决定了ISAM 驱动程序从前几条数据采样确定数据类型,默认为“8”。可以通过修改“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel”下的该注册表值来更改采样行数。但是这种改进还是没有根本上解决问题,即使我们把IMEX设为“1”, TypeGuessRows设得再大,例如1000,假设数据表有1001行,某列前1000行全为纯数字,该列的第1001行又是一个文本,ISAM驱动的这种机制还是让这列的数据变成空。
故此,以上两种解决方式都存在着一定的弊端。经过仔细的查阅资料,总结出如下方法:
解决办法:
从以上的分析中可以得知,当某列数据中含有混合类型时,在.NET中使用Microsoft.Jet.OLEDB.4.0来读取Excel文件造成数据丢失是不可避免的,要解决这个问题只能考虑采用其它数据读取方法。 在.NET中读取Excel文件的另外一种方法是回到使用传统COM组件,这种方法在很多技术文章或论文中都有涉及,本文不作赘述。需要指出的是,使用COM组件来读取Excel文件数据的效率较低,在作释放的时候有可能碰到不可预知的错误,特别开发Web应用的程序应该慎重使用。 (不推荐使用)不过在网上查到了另外一种利用读取CSV纯文本格式解决此问题的方法。 (1)在读取Excel的.xls类型的文本数据之前,先将其转换为.csv格式,在Excel中直接另存为这种格式就可以达到转换的目的。CSV文件又称为逗号分隔的文件,是一种纯文本文件,它以“,”分隔数据列,本文表1的数据表用CSV格式存储后用纯文本编辑器打开的表现形式如表3所示。 表3 采用CSV格式保存的表1数据 需要指出的是,CSV文件也可以用Ole DB或ODBC的方式读取,但是如果采用这些方式读取其数据又会回到丢失数据的老路上,ISAM机制同样会发挥作用。 (2)采用普通的读取文本文件的方法打开文件,读取第一行,用“,”作为分隔符获得各字段名,在DataTable中创建对应的各字段,字段的类型可以统一创建成“String”。 本文原文 (3)逐行读取数据行, 用“,”作为分隔符获得某行各列的数据并填入DataTable相应的字段中。 实现的简化代码如下: String line; String [] split = null; DataTable table=new DataTable("Thylx"); DataRow row=null; StreamReader sr=new StreamReader("c:/Thylx.csv",System.Text.Encoding.Default); //创建与数据源对应的数据列 line = sr.ReadLine(); split=line.Split(','); foreach(String colname in split){ table.Columns.Add(colname,System.Type.GetType("System.String")); } //将数据填入数据表 int j=0; while((line=sr.ReadLine())!=null){ j=0; row = table.NewRow(); split=line.Split(','); foreach(String colname in split){ row[j]=colname; j++;} table.Rows.Add(row);} sr.Close(); //显示数据 dataGrid1.DataSource=table.DefaultView; dataGrid1.DataBind(); 总结: 在应用程序的设计中,需要访问Excel数据的情况非常普遍,本文以在.NET中对访问含有混合类型数据的Excel表格拟采取的方法进行探讨。当然,如果不存在混合类型的数据使用Microsoft.Jet.OLEDB为较佳方案,也就是我上面提到的在网络中查到的大部分做法。本文只是针对NET方面的一些研究,对于其他开发语言,可参照类似的做法来解决excel读取时数据丢失的问题