C#Excel文件读取问题及解决办法
?????? 最近由于工作上用到了讀取Excel操作,完成過程中遇到了各種各樣的問題,最后為了以后讀取Excel時不再如此的麻煩,特意做成了一個小組件,方便日后使用?,F在總結一下過程中遇到的問題及相應的解決辦法。?
????????????????????????????????????????????????????????????????????????????????????????????????? 一、Excel讀取方式及問題
?????? 第一種方式:通過OleDb連接,把excel文件作為數據源來讀取。
?????? 優點:讀取的效率很快。
?????? 缺點:1、讀取過程中需要依賴于計算機安裝的office版本,不同的版本讀取過程中用到的代碼會有所不同,這就需要在實現的過程中必須判斷客戶端機器安裝的office版本(如果客戶端未裝任何版本的office軟件,則程序將會報錯);
?????????????? 2、讀取Excel得到的工作表名稱即Sheet名稱會和Excel表格中實際的Sheet名稱有所不一致 ,不過這并不妨礙程序的運行;
?????????????? 3、如果Excel表格中存在這樣的列:列中的數據類型不一致,比如說列中一些行為數字,一些行為字符串,那么將有一種類型的數據部分解析不出來,顯示為空,產生這種問題的根源與Excel ISAM[3](Indexed Sequential Access Method,即索引順序存取方法)驅動程序的限制有關,Excel ISAM 驅動程序通過檢查前幾行中實際值確定一個 Excel 列的類型,然后選擇能夠代表其樣本中大部分值的數據類型[4]。也即Excel ISAM查找某列前幾行(默認情況下是8行),把占多的類型作為其處理類型。例如如果數字占多,那么其它含有字母等文本的數據項就會置空;相反如果文本居多,純數字的數據項就會被置空。?若要修改默認情況下的8行,需要修改注冊表值TypeGuessRows,TypeGuessRows 值決定了ISAM 驅動程序從前幾條數據采樣確定數據類型,默認為“8”。可以通過修改“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel”下的該注冊表值來更改采樣行數。但是這種改進還是沒有根本上解決問題。遇到這種情況時應當考慮不再使用這種讀取Excel表格的方式。
??????? 第二種方式:引用Com組件讀取Excel文件。
??????? 優點:無需考慮客戶端機器安裝的Office版本,讀取的Sheet名稱和Excel表格中完全一致,也不需要考慮Excel表格中列中的數據類型是否不一致,讀取的數據十分準確。
????? 缺點:1、效率比較低,讀取的過程耗時嚴重;
??????????????2、需要做好內存的釋放、Excel進程的關閉工作。
???????????????????????????????????????????????????????????????????????????????????????????????二、讀取方式詳解及問題解決辦法總結?
????? 第一種方式:通過OleDb連接,把excel文件作為數據源來讀取。
????? 1、方式詳解:該方式主要的部分為兩步:第一步,需要解析Excel表中的所有Sheet名稱,如下面的代碼,我將得到的所有Sheet名稱放到了一個Lsit<string>集合集合中(_workSheets),并在代碼段將該集合綁定到了下拉框cmbxWorkSheets;
string conn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + SourceExcelFile + ";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\"" ;OleDbConnection conn = new OleDbConnection(connString); conn.Open(); DataTable dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); conn.Close();foreach (DataRow row in dt.Rows){string name = row["TABLE_NAME"].ToString();if (!name.Contains("$"))continue;_workSheets.Add(name);}?
第二步,根據選擇的Sheet,讀取excel表格中相應sheet名稱的數據 string conn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + SourceExcelFile + ";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\"" ;string sql = string.Format("select * from [{0}]", cmbxWorkSheets.Text); OleDbDataAdapter da = new OleDbDataAdapter(sql, conn); DataSet ds = new DataSet();da.Fill(ds); if (ds != null && ds.Tables.Count > 0)dt = ds.Tables[0];這樣,讀取過程基本完成。
????? 2、方式問題及解決:
????? 問題1:在上面用到的連接字符串 string?conn =?"Provider=Microsoft.ACE.OLEDB.12.0;Data Source="?+ SourceExcelFile +?";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\""?,其中Provider=Microsoft.ACE.OLEDB.12.0,對于不同的offcie版本,Provider對應的值會有所不同,07及以上版本為Microsoft.ACE.OLEDB.12.0,而03版本的office卻為Microsoft.Jet.OLEDB.4.0,因此需要判斷客戶端安裝的office版本以確定Provider的值,而不能像上面那樣直接寫死。
/// <summary>/// 判斷Excel的版本號。/// </summary>/// <returns></returns>private static double CheckExcelVer(){Type objExcelType = Type.GetTypeFromProgID("Excel.Application");if (objExcelType == null){return 0;}object objApp = Activator.CreateInstance(objExcelType);if (objApp == null){return 0;}object objVer = objApp.GetType().InvokeMember("Version", BindingFlags.GetProperty, null, objApp, null);double iVer = Convert.ToDouble(objVer.ToString());objVer = null;objApp = null;objExcelType = null;GC.Collect();return iVer;}/// <summary>/// 根據Excel版本號獲得Excel的版本名稱。/// </summary>/// <returns></returns>public static String GetExcelVerStr(){String version;double excelver;excelver = CheckExcelVer();// ExistsExcelRegedit();version = "Office ";if (excelver == 0){MessageBox.Show("無法識別Excel的版本", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Information);version = "無法識別 office 版本";}else if (excelver >= 14) version += "2010或以上";else if (excelver >= 12) version += "2007";else if (excelver >= 11) version += "2003";else if (excelver >= 10) version += "XP";else if (excelver >= 9) version += "2000";else if (excelver >= 8) version += "97";else if (excelver >= 7) version += "95";return version;}/// <summary>/// 根據Excel的版本名稱獲得Provider。/// </summary>/// <returns></returns>public static string GetExcelProvider(){string provider = string.Empty;string version = GetExcelVerStr();switch (version){case "Office 2010或以上":case "Office 2007":provider = "Microsoft.ACE.OLEDB.12.0";break;case "Office 2003":case "Office 2000":case "Office XP":case "Office 97":case "Office 95":provider = "Microsoft.Jet.OLEDB.4.0";break;}return provider;}這樣就可以根據用戶客戶端安裝的office版本來讀取Excel,只要客戶端安裝了office解析Excel時便不會出錯。
??????問題二:Excel表格中某些列中數據類型不一致,導致列中后面的數據讀取不出來。這種方式在讀取Excel表的時候,無法定義和修改讀取列的數據類型,string?conn =?"Provider=Microsoft.ACE.OLEDB.12.0;Data Source="?+ SourceExcelFile +?";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\""?,雖然“IMEX = 1”被認為是用來解決數字與字符混合時,識別不正常的情況,但其實它并未真正解決問題,表現為如果某字段前8條記錄中全部為純數字的話,那么在該字段隨后的記錄中含有字母或漢字的項將仍然變為空,但是如果該字段前8條記錄中有一條不為純數字,將能得到預期想要的結果,也就是說它其實只對前8行中不一致的數據類型起作用,8行之后的數據若出現數據不一致,便會出現為空的情況。此時,若要更好的解決問題,需要用第二種方式來讀取Excel表。
??????? 第二種方式:引用Com組件讀取Excel文件。
????? 1、方式詳解
????? 這種方式需要添加COM中的程序集Microsoft Excel 11.0 Object Library.dll,修改程序集中的屬性“嵌入互操作類型”為False,不然會提示錯誤:?無法嵌入互操作類型“Microsoft.Office.Interop.Excel.ApplicationClass”。讀取Excel的代碼:
private Microsoft.Office.Interop.Excel.ApplicationClass _excelApp;private Microsoft.Office.Interop.Excel._Workbook _workbook;private Microsoft.Office.Interop.Excel.Sheets _workSheets;_excelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();_excelApp.Visible = true;_workbook = _excelApp.Workbooks.Open(fileName, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value) as Microsoft.Office.Interop.Excel.WorkbookClass;_workSheets = _workbook.Sheets;private void ReadExcel(){Excel.Range selectedRange = _excelApp.Selection as Excel.Range;try{if (ResultTable != null){ResultTable.Clear();ResultTable.Dispose();}ResultTable = new DataTable();string colName = string.Empty;if (IncludeHeader){for (int i = 1; i <= selectedRange.Columns.Count; i++){Excel.Range cell = selectedRange[1, i] as Excel.Range;if (cell != null && cell.Value != null)colName = cell.Value.ToString().Trim();elsecolName = string.Empty;if (string.IsNullOrEmpty(colName))colName = "列 " + i.ToString();if (ResultTable.Columns.Contains(colName))throw new Exception(string.Format("導入數據存在重復的列:{0}。", colName));ResultTable.Columns.Add(new DataColumn(colName));}}else // 若不含標題行,則列名自動命名為“列 1”、“列 2”等。for (int i = 1; i <= selectedRange.Columns.Count; i++)ResultTable.Columns.Add(new DataColumn("列 " + i.ToString()));int firstDataLineNo = IncludeHeader ? 2 : 1; // 若不含標題行,數據從第一行開始。int count = selectedRange.Rows.Count;for (int i = firstDataLineNo; i <= count; i++){//if (((Excel.Range)selectedRange[i, 1]).Value == null)// continue; DataRow dr = ResultTable.NewRow();for (int j = 0; j < selectedRange.Columns.Count; j++)dr[j] = ((Excel.Range)selectedRange[i, j + 1]).Value;ResultTable.Rows.Add(dr);SetProgressValue(count - firstDataLineNo, i - firstDataLineNo);}}catch (Exception exp){MessageBox.Show(this, string.Format("讀Excel錯誤:{0}", exp.Message), "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);_excelApp.Visible = true;}}?????? 方式問題及解決:
?????? 這種方式效率很慢,而且需要做好善后工作。釋放內存:
private void DisposeExcelResource(){try{if (_workbook != null)_workbook.Close(false, Missing.Value, false);if (_excelApp != null)_excelApp.Quit();System.Runtime.InteropServices.Marshal.ReleaseComObject(_excelApp);System.Runtime.InteropServices.Marshal.ReleaseComObject(_workbook);//System.Runtime.InteropServices.Marshal.ReleaseComObject( _selectedRange ); GC.Collect();}catch{}}????? 但上面這些做不到關閉打開的Excel文件,若需要做到關閉Excel文件:
[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);public void BeforeClosing(){if (_excelApp != null){IntPtr t = new IntPtr(_excelApp.Hwnd);int k = 0;GetWindowThreadProcessId(t, out k);System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(k);p.Kill();}}?????????????????????????????????????????????????????????????????????????????????????????????????? 三、總結
????????花了一定的時間,工作上想實現的實現了,同時也將問題的解決總結了下來。這個過程自己有很大的收獲,以后遇到問題時也一定要像現在這樣將問題深入全面的了解并解決,多解決問題,多總結解決問題的過程,這樣才能讓自己寒酸的技術和表達能力一點一點的得到提高。
???????
??
??????
???
?
?
?
?
?
轉載于:https://www.cnblogs.com/huachen/p/3382363.html
總結
以上是生活随笔為你收集整理的C#Excel文件读取问题及解决办法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 经常手抖是什么原因引起的?
- 下一篇: phpcms v9 内容页显示会员的详细