徹底解決Oledb連接Excel數據類型不統一的問題

http://www.klipdas.com/blog/?p=%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3oledb%E8%BF%9E%E6%8E%A5excel%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%B8%8D%E7%BB%9F%E4%B8%80%E7%9A%84%E9%97%AE%E9%A2%98

 

 

在使用Microsoft.Jet.OLEDB.4.0連接Excel,進行讀取數據,相對使用傳統的COM來讀取數據,效率是很高的。但相對傳統COM操作Excel來說,及存在數據類型轉換的問題。

因爲使用OLEDB連接Excel讀取數據時,需要確定數據的類型。默認情況使用連接字符串:

1.string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + excelFile + ";Extended Properties='Excel 8.0;'";

 使用上面的連接字符串連接Excel時,可能會遇到數據類型不一致的問題。所謂數據類型不一致,是指同一列裏面數據類型可能出現多種,如浮點數、字符串、日期等;當出現此類情況時,讀取出來的數據就爲空,甚至會報錯,如“非法的日期格式”等異常。出現這種問題,我們大家都會想到把數據全部都按字符數據來讀取,但是按什麼數據類型來讀取不是我們能控制的,是OLEDB控制的,至少暫時我還沒有找到能控制輸出數據類型的方法。因爲我當初也嘗試使用convert,cast函數對輸出的列進行類型轉換,但oledb連接Excel時,使用的SQL不支持這些函數。因此只能從其他角度來解決該問題。我也在網上搜索了很多解決方法,最全面的解決方法是:http://www.douban.com/note/18510346/。下面列出了網上出現解決該問題方法的比較:

 

解決方案說明缺點
COM 使用Excel COM接口訪問Excel 非託管、不容易釋放資源、效率低下
連接字符串添加IMEX=1 構造的連接字符串,如:
1.string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + excelFile + ";Extended Properties='Excel 8.0;HDR=YES;IMEX=1'";
其中HDR表示是否將Sheet頁的第一行作爲字段名,“YES”代表是,“NO”代表不是,當爲YES時,將把SHEET頁的第一行作爲字段名,數據從第二行開始,而如果是NO時,字段名就是要SHEET的列名,如A,B,C等,數據就從第一行開始取;IMEX是用來告訴驅動程序,使用Excel文件的模式,其值有0、1、2三種,分別代表導出、導入、混合模式。當我們設置IMEX=1時將強制混合數據轉換爲文本,但僅僅這種設置並不可靠,IMEX=1只確保在某列前8行數據至少有一個是文本項的時候才起作用,它只是把查找前8行數據中數據類型佔優選擇的行爲作了略微的改變。例如某列前8行數據全爲純數字,那麼它仍然以數字類型作爲該列的數據類型,隨後行裏的含有文本的數據仍然變空。 (摘至:http://www.douban.com/note/18510346/)。
只根據前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驅動的這種機制還是讓這列的數據變成空。 (摘至:http://www.douban.com/note/18510346/)。 修改註冊表不方便,而且無法事先判讀sheet有多少行,因此還是受行數限制。
將Excel先轉換成csv純文本格式

(1)在讀取Excel的.xls類型的文本數據之前,先將其轉換爲.csv格式,在Excel中直接另存爲這種格式就可以達到轉換的目的。CSV文件又稱爲逗號分隔的文件,是一種純文本文件,它以“,”分隔數據列。

  

需要指出的是,CSV文件也可以用Ole DB或ODBC的方式讀取,但是如果採用這些方式讀取其數據又會回到丟失數據的老路上,ISAM機制同樣會發揮作用。

  

(2)採用普通的讀取文本文件的方法打開文件,讀取第一行,用“,”作爲分隔符獲得各字段名,在DataTable中創建對應的各字段,字段的類型可以統一創建成“String”。

(3)逐行讀取數據行, 用“,”作爲分隔符獲得某行各列的數據並填入DataTable相應的字段中。

簡要代碼:

01.String line; 
02.String [] split = null
03.DataTable table=new DataTable("auto"); 
04.DataRow row=null
05.StreamReader sr=new StreamReader("c:/auto.csv",System.Text.Encoding.Default); 
06.//創建與數據源對應的數據列 
07.line = sr.ReadLine(); 
08.split=line.Split(','); 
09.foreach(String colname in split){ 
10.  table.Columns.Add(colname,System.Type.GetType("System.String")); 
11.
12.//將數據填入數據表 
13.int j=0; 
14.while((line=sr.ReadLine())!=null){ 
15.  j=0; 
16.  row = table.NewRow(); 
17.  split=line.Split(','); 
18.  foreach(String colname in split){ 
19.      row[j]=colname; 
20.      j++;
21.   
22.  table.Rows.Add(row);
23.
24.sr.Close(); 
25.//顯示數據 
26.dataGrid1.DataSource=table.DefaultView; 
27.dataGrid1.DataBind();
(摘至:http://www.douban.com/note/18510346/)。
需要事先將excel轉換成csv文件

 

這裏提供一個更加方便的辦法,不過前提是第一行必須是作爲字段名或者第一行的數據類型就爲字符型。這樣一說,大家就明白了。首先修改連接字符串爲:

1.string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + excelFile + ";Extended Properties='Excel 8.0;HDR=NO;IMEX=1'";

這裏將HDR設爲NO,因爲我就是將第一行做爲數據讀取,而IMEX=1就表示根據前8行判斷列的數據類型,如果有字符型數據,那麼就強制混合數據轉換爲文本。這裏就明白爲什麼要保證第一行爲字符型的原因了。能將列的數據類型強制設爲字符型,那麼列中出現什麼類型的數據都不怕了。需要做的工作就是,在獲取完數據後,將字段名重新設置,並刪除第一條記錄即可。代碼如下:

01.DataTable dt = new DataTable();
02.  
03.using(OleDbCommand cmd = new OleDbCommand()){
04.    cmd.Connection = conn;
05.    cmd.CommandType = CommandType.Text;
06.    cmd.CommandTimeout = 6;
07.    cmd.CommandText = string.Format("select * from [{0}$]", sheetName);
08.  
09.    OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
10.    adapter.Fill(dt);
11.}
12.  
13.if (dt.Rows.Count > 0) {
14.    DataRow dr = dt.Rows[0];
15.  
16.    for (int col = 0; col < dt.Columns.Count; col++) {
17.        dt.Columns[col].ColumnName = dr[col].ToString();
18.    }
19.  
20.    dt.Rows[0].Delete();
21.    dt.AcceptChanges();
22.}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章