1 引言
在應用程序的設計中,常常須要讀取Excel數據或將Excel數據導入轉換到其餘數據載體中,例如將Excel數據經過應用程序導入SQL Sever等數據庫中以備使用。筆者在開發「汽車產業鏈ASP協同商務平臺」中遇到了相似需求。某汽車整車生產企業須要將其車輛發車信息發佈到汽車產業鏈平臺上去,其數據爲內部ERP系統生成的Excel數據表,用戶首先將該數據表上傳至汽車產業鏈平臺,平臺將此Excel數據讀取導入到平臺內部的SQL Sever數據庫中,以供其它應用使用。汽車產業鏈平臺的開發使用的開發工具爲VS.NET,使用的語言是C#,在開發的過程當中發現使用Microsoft.Jet.OLEDB.4.0讀取數據會出現當某一字段內分別含有文本和數字的混合數據時,某一類型的數據會產生丟失。本文就對此問題產生的根源進行了分析並給出了相應的解決方法。
2 問題描述
Excel是Microsoft公司的電子表格處理軟件,在現代辦公及企業信息化的應用中使用很是普遍,正因如此,在程序設計中咱們常常要經過訪問Excel文件來得到數據,但Excel文件不是標準數據庫[1]。
ASP.NET也是Microsoft公司的產品,做爲.NET FrameWork框架中的一個重要組成部分,其主要用於Web設計。咱們在.NET中訪問讀取Excel數據時通常採用Microsoft.Jet.OLEDB.4.0[2]。現以讀取一個Excel文件auto.xls中sheet1工做表爲例,工做表的內容如表1所示。
表1 sheet1表的數據內容
現將該表的數據內容讀取並顯示到到DataGrid中,簡化的代碼以下:
String ConnStr = " Provider = Microsoft.Jet.OLEDB.4.0; DataSource=c:/auto.xls;Extended Properties='Excel 8.0;HDR=YES';";
OleDbConnection Conn=new OleDbConnection(ConnStr);
Conn.Open();
string SQL="select * from [sheet1$]";
OleDbDataAdapter da=new OleDbDataAdapter(SQL,ConnStr);
DataSet ds=new DataSet();
da.Fill(ds);
DataGrid1.DataSource=ds;
DataGrid1.DataBind();
Conn.Close();
可是運行以上代碼的結果並非指望的,它將顯示爲表2所示的內容。能夠發現第一個字段中爲「1042」的兩個數據項變爲空。
表2 DataGrid1所顯示的數據內容
有程序設計人員將以上代碼OleDbConnection鏈接字符串中的Extended Properties一項做了以下改動,Extended Properties='Excel 8.0;HDR=NO;IMEX=1’,認爲能夠解決此問題。因爲在開發「汽車產業鏈協同商務平臺」中碰到過相似問題,做了大量的測試後發現,添加IMEX=1後並未實質上解決此問題。表現爲:若是某字段前8條記錄中所有爲純數字的話,那麼在該字段隨後的記錄中含有字母或漢字的項將仍然變爲空,可是若是該字段前8條記錄中有一條不爲純數字,將能獲得預期想要的結果。
3 問題分析
產生這種問題的根源與Excel ISAM[3](Indexed Sequential Access Method,即索引順序存取方法)驅動程序的限制有關,Excel ISAM 驅動程序經過檢查前幾行中實際值肯定一個 Excel 列的類型,而後選擇可以表明其樣本中大部分值的數據類型[4]。也即Excel ISAM查找某列前幾行(默認狀況下是8行),把佔多的類型做爲其處理類型。例如若是數字佔多,那麼其它含有字母等文本的數據項就會置空;相反若是文本居多,純數字的數據項就會被置空。
現具體分析在第1節程序代碼Extended Properties項中的HDR和IMEX所表明的含義。HDR用來設置是否將Excel表中第一行做爲字段名,「YES」表明是,「NO」表明不是即也爲數據內容;IMEX是用來告訴驅動程序使用Excel文件的模式,其值有0、一、2三種,分別表明導出、導入、混合模式。當咱們設置IMEX=1時將強制混合數據轉換爲文本,但僅僅這種設置並不可靠,IMEX=1只確保在某列前8行數據至少有一個是文本項的時候才起做用,它只是把查找前8行數據中數據類型佔優選擇的行爲做了略微的改變。例如某列前8行數據全爲純數字,那麼它仍然以數字類型做爲該列的數據類型,隨後行裏的含有文本的數據仍然變空。
另外一個改進的措施是IMEX=1與註冊表值TypeGuessRows配合使用,TypeGuessRows 值決定了ISAM 驅動程序從前幾條數據採樣肯定數據類型,默認爲「8」。能夠經過修改「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel」下的該註冊表值來更改採樣行數。可是這種改進仍是沒有根本上解決問題,即便咱們把IMEX設爲「1」, TypeGuessRows設得再大,例如1000,假設數據表有1001行,某列前1000行全爲純數字,該列的第1001行又是一個文本,ISAM驅動的這種機制仍是讓這列的數據變成空。
4 解決方法
從以上的分析中能夠得知,當某列數據中含有混合類型時,在.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("auto");
DataRow row=null;
StreamReader sr=new StreamReader("c:/auto.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();
5 結語
在應用程序的設計中,須要訪問Excel數據的狀況很是廣泛,本文以在.NET中對訪問含有混合類型數據的Excel表格擬採起的方法進行探討。固然,若是不存在混合類型的數據使用Microsoft.Jet.OLEDB爲較佳方案。對於不是使用.NET開發的狀況,本論文的分析和所提供的方法亦可參考。
OLEDB 鏈接EXCEL的鏈接字符串 IMEX的問題
今天碰到一個問題須要想EXCEL表中寫數據,折騰了很久才發現是IMEX惹得禍,因此記錄下提醒本身,也但願你們不要出一樣的錯。
碰到問題:使用語句 "insert into [Sheet1$] (大類) values ('test')" 沒法插入 。
緣由:Provider=Microsoft.Jet.OLEDB.4.0;Data Source='2008-08.xls'; Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'
解決方法: 去掉IMEX=1
補充: 向EXCEL插入數據時 數據類型是由前8行數據中數據類型佔優選擇 例如:分數一列前前8行爲空值 插入5爲字符串格式,若是前8行爲數字格式 插入5爲數字格式關於IMEX的資料: IMEX是用來告訴驅動程序使用Excel文件的模式,其值有0、一、2三種,分別表明導出、導入、混合模式。當咱們設置IMEX=1時將強制混合數據轉換爲文本,但僅僅這種設置並不可靠,IMEX=1只確保在某列前8行數據至少有一個是文本項的時候才起做用,它只是把查找前8行數據中數據類型佔優選擇的行爲做了略微的改變。例如某列前8行數據全爲純數字,那麼它仍然以數字類型做爲該列的數據類型,隨後行裏的含有文本的數據仍然變空。 另外一個改進的措施是IMEX=1與註冊表值TypeGuessRows配合使用,TypeGuessRows 值決定了ISAM 驅動程序從前幾條數據採樣肯定數據類型,默認爲「8」。能夠經過修改「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \Jet\4.0\Engines\Excel」下的該註冊表值來更改採樣行數。可是這種改進仍是沒有根本上解決問題,即便咱們把IMEX設爲「1」, TypeGuessRows設得再大,例如1000,假設數據表有1001行,某列前1000行全爲純數字,該列的第1001行又是一個文本,ISAM驅動的這種機制仍是讓這列的數據變成空。