在使用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'" ; |
只根據前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(); |
需要事先將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.
}