日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#Excel文件读取问题及解决办法

發(fā)布時間:2025/5/22 C# 86 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#Excel文件读取问题及解决办法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?????? 最近由于工作上用到了讀取Excel操作,完成過程中遇到了各種各樣的問題,最后為了以后讀取Excel時不再如此的麻煩,特意做成了一個小組件,方便日后使用。現(xiàn)在總結(jié)一下過程中遇到的問題及相應(yīng)的解決辦法。?

????????????????????????????????????????????????????????????????????????????????????????????????? 一、Excel讀取方式及問題

?????? 第一種方式:通過OleDb連接,把excel文件作為數(shù)據(jù)源來讀取

?????? 優(yōu)點:讀取的效率很快。

?????? 缺點:1、讀取過程中需要依賴于計算機安裝的office版本,不同的版本讀取過程中用到的代碼會有所不同,這就需要在實現(xiàn)的過程中必須判斷客戶端機器安裝的office版本(如果客戶端未裝任何版本的office軟件,則程序?qū)箦e);

?????????????? 2、讀取Excel得到的工作表名稱即Sheet名稱會和Excel表格中實際的Sheet名稱有所不一致 ,不過這并不妨礙程序的運行;

?????????????? 3、如果Excel表格中存在這樣的列:列中的數(shù)據(jù)類型不一致,比如說列中一些行為數(shù)字,一些行為字符串,那么將有一種類型的數(shù)據(jù)部分解析不出來,顯示為空,產(chǎn)生這種問題的根源與Excel ISAM[3](Indexed Sequential Access Method,即索引順序存取方法)驅(qū)動程序的限制有關(guān),Excel ISAM 驅(qū)動程序通過檢查前幾行中實際值確定一個 Excel 列的類型,然后選擇能夠代表其樣本中大部分值的數(shù)據(jù)類型[4]。也即Excel ISAM查找某列前幾行(默認情況下是8行),把占多的類型作為其處理類型。例如如果數(shù)字占多,那么其它含有字母等文本的數(shù)據(jù)項就會置空;相反如果文本居多,純數(shù)字的數(shù)據(jù)項就會被置空。?若要修改默認情況下的8行,需要修改注冊表值TypeGuessRowsTypeGuessRows 值決定了ISAM 驅(qū)動程序從前幾條數(shù)據(jù)采樣確定數(shù)據(jù)類型,默認為“8”。可以通過修改“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel”下的該注冊表值來更改采樣行數(shù)。但是這種改進還是沒有根本上解決問題。遇到這種情況時應(yīng)當考慮不再使用這種讀取Excel表格的方式。

??????? 第二種方式:引用Com組件讀取Excel文件。

??????? 優(yōu)點:無需考慮客戶端機器安裝的Office版本,讀取的Sheet名稱和Excel表格中完全一致,也不需要考慮Excel表格中列中的數(shù)據(jù)類型是否不一致,讀取的數(shù)據(jù)十分準確。

????? 缺點:1、效率比較低,讀取的過程耗時嚴重;

??????????????2、需要做好內(nèi)存的釋放、Excel進程的關(guān)閉工作。

???????????????????????????????????????????????????????????????????????????????????????????????二、讀取方式詳解及問題解決辦法總結(jié)?

????? 第一種方式:通過OleDb連接,把excel文件作為數(shù)據(jù)源來讀取

????? 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);}

?

第二步,根據(jù)選擇的Sheet,讀取excel表格中相應(yīng)sheet名稱的數(shù)據(jù) 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對應(yīng)的值會有所不同,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>/// 根據(jù)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>/// 根據(jù)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;}

這樣就可以根據(jù)用戶客戶端安裝的office版本來讀取Excel,只要客戶端安裝了office解析Excel時便不會出錯。

??????問題二:Excel表格中某些列中數(shù)據(jù)類型不一致,導致列中后面的數(shù)據(jù)讀取不出來。這種方式在讀取Excel表的時候,無法定義和修改讀取列的數(shù)據(jù)類型,string?conn =?"Provider=Microsoft.ACE.OLEDB.12.0;Data Source="?+ SourceExcelFile +?";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\""?,雖然“IMEX = 1”被認為是用來解決數(shù)字與字符混合時,識別不正常的情況,但其實它并未真正解決問題,表現(xiàn)為如果某字段前8條記錄中全部為純數(shù)字的話,那么在該字段隨后的記錄中含有字母或漢字的項將仍然變?yōu)榭?#xff0c;但是如果該字段前8條記錄中有一條不為純數(shù)字,將能得到預(yù)期想要的結(jié)果,也就是說它其實只對前8行中不一致的數(shù)據(jù)類型起作用,8行之后的數(shù)據(jù)若出現(xiàn)數(shù)據(jù)不一致,便會出現(xiàn)為空的情況。此時,若要更好的解決問題,需要用第二種方式來讀取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("導入數(shù)據(jù)存在重復的列:{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; // 若不含標題行,數(shù)據(jù)從第一行開始。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;}}

?????? 方式問題及解決:

?????? 這種方式效率很慢,而且需要做好善后工作。釋放內(nèi)存:

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{}}

????? 但上面這些做不到關(guān)閉打開的Excel文件,若需要做到關(guān)閉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();}}

?????????????????????????????????????????????????????????????????????????????????????????????????? 三、總結(jié)
????????花了一定的時間,工作上想實現(xiàn)的實現(xiàn)了,同時也將問題的解決總結(jié)了下來。這個過程自己有很大的收獲,以后遇到問題時也一定要像現(xiàn)在這樣將問題深入全面的了解并解決,多解決問題,多總結(jié)解決問題的過程,這樣才能讓自己寒酸的技術(shù)和表達能力一點一點的得到提高。

???????


??


??????

???

?

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/huachen/p/3382363.html

總結(jié)

以上是生活随笔為你收集整理的C#Excel文件读取问题及解决办法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。