C#實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集詳解
功能需求
將Excel里的worksheet表格導(dǎo)入到DataSet里,是項(xiàng)目應(yīng)用里常用的一種操作。一般情況下,worksheet是一個(gè)標(biāo)準(zhǔn)的二維數(shù)組,如下圖:
我們可以效仿 MS SQL SERVER 的一些基本導(dǎo)入選項(xiàng),如首行是否包含數(shù)據(jù),要導(dǎo)入哪個(gè)Sheet?還是遍歷Sheets?
實(shí)際的情況,客戶經(jīng)常會(huì)提供一些合并單元格的Excel表格,如下圖中的“所在部門名稱”列:
再暢想一下,假設(shè)有跨列的情況如下:
解決導(dǎo)入,一種方法,是讓客戶進(jìn)行單元格拆分或技術(shù)服務(wù)人員進(jìn)行拆分后再導(dǎo)入。另一種就是我們要繼續(xù)完善應(yīng)用,處理實(shí)現(xiàn)合并單元格的自動(dòng)化處理。
Excel與DataSet的映射關(guān)系
下圖是 Excel 與 DataSet 的映射關(guān)系圖:
1、Excel應(yīng)用的Workbook對(duì)象與 DataSet 同為容器對(duì)象
2、Worksheets和Tables均代表各自的表集合
3、Worksheet與Table進(jìn)行對(duì)應(yīng),產(chǎn)生和導(dǎo)入實(shí)際的數(shù)據(jù)
范例運(yùn)行環(huán)境
操作系統(tǒng): Windows Server 2019 DataCenter
操作系統(tǒng)上安裝 Office Excel 2016
.net版本: .netFramework4.7.1 或以上
開發(fā)工具:VS2019 C#
Excel DCOM 配置
請(qǐng)參考我的文章《C# 讀取Word表格到DataSet》有對(duì)Office DCOM詳細(xì)配置介紹,這里不再贅述,Excel的對(duì)應(yīng)配置名稱如下圖所示:
設(shè)計(jì)實(shí)現(xiàn)
組件庫(kù)引入
方法設(shè)計(jì)
設(shè)計(jì) object[] ExcelAsDataSet(string _filename,bool hastitle,string startaddress,string endaddress) 方法
返回值
方法返回object數(shù)組,共包括兩個(gè)object對(duì)象,如果成功轉(zhuǎn)化則 object[0] 存儲(chǔ) DataSet對(duì)象,否則為 null。如果不成功則 object[1] 存儲(chǔ)string 錯(cuò)誤信息對(duì)象,可根據(jù)object[1].ToString()!="" 來判斷是否轉(zhuǎn)化成功。
參數(shù)設(shè)計(jì)
1.string _filename:Excel 數(shù)據(jù)源文件路徑
2.bool hastitle: 是否包含標(biāo)題,如果設(shè)置為true,則表示首行數(shù)據(jù)為列名稱定義
3.string startaddress:可指定有效的起始單元格地址,不設(shè)置則默認(rèn)為“A1”(即第一個(gè)單元格)
4.string endaddress:可指定有效的截止單元格地址,不設(shè)置則默認(rèn)為最后一個(gè)有值單元格(即XlCellType.xlCellTypeLastCell 枚舉)
通過3、4參數(shù)的定義,可以定義出有效的導(dǎo)入矩形區(qū)域。
打開數(shù)據(jù)源并計(jì)算Sheets
object[] rv=new object[2]; rv[0]=null; rv[1]=""; //創(chuàng)建一個(gè)名為ExcelApp的組件對(duì)象 // ExcelApplication excel = new ExcelApplication(); Excel.Application excel = new Excel.Application(); excel.DisplayAlerts=false; excel.AskToUpdateLinks=false; Excel.Workbook xb=excel.Workbooks.Add(_filename); //獲取活動(dòng)的 worksheet和 excel sheet的個(gè)數(shù),準(zhǔn)備遍歷sheets Worksheet worksheet = (Worksheet) excel.ActiveSheet; sheetCount=excel.Sheets.Count; int startSheetIndex=1; int endSheetIndex=sheetCount; DataSet ds=new DataSet(); //遍歷sheets for (int currentIndex = startSheetIndex; currentIndex <= endSheetIndex; currentIndex++) { worksheet = (Worksheet)excel.Worksheets[currentIndex]; worksheet.Activate(); //處理每一個(gè)sheet..... }
拆分合并的單元格
在獲取有效的單元格區(qū)域后,就開始遍歷單元格對(duì)象,判斷單元格對(duì)象 MergeCells 屬性即可,判斷 Cell.MergeCells.ToString() == "True" 即表示該單元格為合并單元格對(duì)象。
示例代碼如下:
//獲取起始單元和截止單元格,以確定有效區(qū)域 Excel.Range _startcell=worksheet.Range["A1","A1"]; //默認(rèn)為第一個(gè)單元格 if(startaddress!="") { try { _startcell=worksheet.Range[startaddress,startaddress]; } catch(Exception ex) { rv[1]+=string.Format("{1}指定的起始單元格地址{0},不是合法的地址。\r\n",startaddress,worksheet.Name); // KillProcessByStartTime("EXCEL",beforetime,aftertime); continue; } } Excel.Range _lastcell=worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing); //默認(rèn)獲取有值的最后一個(gè)有效的單元格 if(endaddress!="") { try { _lastcell=worksheet.Range[endaddress,endaddress]; } catch(Exception ex) { rv[1]+=string.Format("{1}指定的結(jié)束單元格地址{0},不是合法的地址。\r\n",endaddress,worksheet.Name); // KillProcessByStartTime("EXCEL",beforetime,aftertime); // return rv; continue; } } //遍歷有效區(qū)域單元格 foreach (Excel.Range aicell in worksheet.Range[_startcell,_lastcell]) { if (aicell.MergeCells.ToString() == "True") { //處理合并單元格 object temp_merge_value = aicell.Value2; //備份單元格的值 int u_row = aicell.Row; //記錄單元格的首行索引 int u_rows = aicell.MergeArea.Rows.Count; //記錄單元格的合并區(qū)域包含的行數(shù) int u_col = aicell.Column; //記錄單元格的首列索引 int u_cols = aicell.MergeArea.Columns.Count; //記錄單元格的合并區(qū)域包含的列數(shù) aicell.MergeArea.UnMerge(); //取消合并,拆分單元格 Excel.Range new_aicell = worksheet.Range[worksheet.Cells[u_row, u_col], worksheet.Cells[u_row + u_rows - 1, u_col + u_cols - 1]]; //獲取拆分后單元格后的有效區(qū)域 new_aicell.Value2 = temp_merge_value; //將拆分的單元格重新賦值(備份值) } }
創(chuàng)建DataTable
如果首行是列數(shù)據(jù),則以該行的值創(chuàng)建表結(jié)構(gòu),否則自動(dòng)創(chuàng)建以“C”為前綴的列名,如C1、C2...Cn以此類推。
System.Data.DataTable dt=ds.Tables.Add(); dt.TableName=worksheet.Name; //表名為worksheet的名稱 for(int i=_startcell.Column;i<=_lastcell.Column;i++) { Excel.Range _cell=worksheet.Range[worksheet.Cells[_startcell.Row,i],worksheet.Cells[_startcell.Row,i]]; string _colname=hastitle==true?_cell.Value2.ToString():"C"+(i-_startcell.Column+1).ToString(); //如果第一行是標(biāo)題,則賦單元格的值,否則以C開頭加序號(hào) DataColumn dc=dt.Columns.Add(); dc.ColumnName=_colname; dc.DataType=System.Type.GetType("System.String"); dc.AllowDBNull=true; }
將單元格數(shù)據(jù)寫入DataTable
object[,] cells=null; 定義二維對(duì)象數(shù)組 if(hastitle) //如果首行包含列,則加行索引加1取數(shù)據(jù)行 { startrow=_startcell.Row+1; } //將有效區(qū)域單元格轉(zhuǎn)化賦值為 object[,] cells=(object[,])worksheet.Range[worksheet.Cells[startrow,_startcell.Column],worksheet.Cells[_lastcell.Row,_lastcell.Column]].Value2; //遍歷數(shù)組,添加行數(shù)據(jù)到 DataTable里 int _rowcount=cells.GetLength(0); int _colcount=cells.GetLength(1); for(int i=0;i<_rowcount;i++) { object[] newrowdata=new object[_colcount]; for(int j=0;j<_colcount;j++) { newrowdata[j]=cells[i,j]; } DataRow dr=dt.Rows.Add(newrowdata); }
總結(jié)
在實(shí)際的應(yīng)用中,還可以設(shè)定多種參數(shù)選項(xiàng):
1、如導(dǎo)入單元格的數(shù)據(jù),是格式化后的數(shù)據(jù)(ExcelReport.ImportDataType.FormattingValue),還是原始數(shù)據(jù)(ExcelReport.ImportDataType.OriginalValue),這也是Cell.Value和Cell.Value2的區(qū)別
2、創(chuàng)建表列名字段過度依賴于單元格的值,可能會(huì)創(chuàng)建失敗,建議定義參數(shù)指定是否重寫列名
3、是否只導(dǎo)入指定的sheet或活動(dòng)的sheet。
這些選項(xiàng)都可以根據(jù)實(shí)際的業(yè)務(wù)進(jìn)行擴(kuò)展,我們?cè)诖藘H講述了一些操作Excel相關(guān)的關(guān)鍵方法和屬性,這里僅作參考,歡迎大家評(píng)論指教!
以上就是C#實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集詳解的詳細(xì)內(nèi)容,更多關(guān)于C#合并Excel單元格數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#泛型集合類System.Collections.Generic
這篇文章介紹了C#中的泛型集合類System.Collections.Generic,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05c#實(shí)現(xiàn)圖片的平移和旋轉(zhuǎn)示例代碼
這篇文章主要給大家介紹了關(guān)于c#實(shí)現(xiàn)圖片的平移和旋轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用c#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08C#中高精度計(jì)時(shí)器Stopwatch的用法詳解
偶然發(fā)現(xiàn)C# 的計(jì)時(shí)器類Stopwatch,他特別適合測(cè)量運(yùn)行時(shí)間,使用簡(jiǎn)單、計(jì)時(shí)精確,下面就跟隨小編一起來學(xué)習(xí)一下它的具體應(yīng)用吧2024-11-11C#實(shí)現(xiàn)動(dòng)態(tài)加載dll的方法
這篇文章主要介紹了C#實(shí)現(xiàn)動(dòng)態(tài)加載dll的方法,涉及針對(duì)動(dòng)態(tài)鏈接庫(kù)的靈活操作技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12winform實(shí)現(xiàn)可拖動(dòng)的自定義Label控件
這篇文章主要為大家詳細(xì)介紹了winform實(shí)現(xiàn)可拖動(dòng)的自定義Label控件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03DevExpress的DateEdit設(shè)置顯示日期和時(shí)間的方法
本文主要介紹了DevExpress的DateEdit設(shè)置顯示日期和時(shí)間的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08C#實(shí)現(xiàn)TCP客戶端和服務(wù)器的基本功能
本文將介紹如何使用C#實(shí)現(xiàn)TCP客戶端和服務(wù)器的基本功能,客戶端與服務(wù)器可以相互發(fā)送消息,文章通過代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12