C#實(shí)現(xiàn)完善Excel不規(guī)則合并單元格數(shù)據(jù)導(dǎo)入的示例代碼
功能完善
在我的文章 《C#實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集》里講述了可以將具有合并單元格的Excel文件數(shù)據(jù)導(dǎo)入到DataSet里,在實(shí)際使用情況中遇到如下情況,如下圖:

如圖中的 H 列,它是一個合并單元格,但它也屬于一個特殊的單元格,即 worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing) 最后一個內(nèi)容單元格,由于其合并單元格的地址問題(),導(dǎo)入程序可能會計(jì)算成為一行數(shù)據(jù):

如圖 H1 列假設(shè)為標(biāo)題列(字段名),那么 H2 列為數(shù)據(jù)行的第一行,作為最后一個單元格,因此可能會被識別為只有一行數(shù)據(jù),而忽略后面所有的行。
因此比較快速的一種解決方案是在右側(cè)增加一個虛擬列:

如圖通過增加列標(biāo)題(“虛擬列”)達(dá)到重新計(jì)算出 最后單元格 ,根據(jù)最后單元格的地址,可以計(jì)算出新的行數(shù),以達(dá)到計(jì)算出正確行數(shù)據(jù)的目的。
Excel與DataSet的映射關(guān)系
下圖是 Excel 與 DataSet 的映射關(guān)系圖:
1、Excel應(yīng)用的Workbook對象與 DataSet 同為容器對象
2、Worksheets和Tables均代表各自的表集合
3、Worksheet與Table進(jìn)行對應(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 配置
請參考我的文章《C# 讀取Word表格到DataSet》有對Office DCOM詳細(xì)配置介紹,這里不再贅述,Excel的對應(yīng)配置名稱如下圖所示:

設(shè)計(jì)實(shí)現(xiàn)
組件庫引入

方法更新
設(shè)計(jì) object[] ExcelAsDataSet(string _filename,bool hastitle,string startaddress,string endaddress) 方法
返回值
方法返回object數(shù)組,共包括兩個object對象,如果成功轉(zhuǎn)化則 object[0] 存儲 DataSet對象,否則為 null。如果不成功則 object[1] 存儲string 錯誤信息對象,可根據(jù)object[1].ToString()!="" 來判斷是否轉(zhuǎn)化成功。
參數(shù)設(shè)計(jì)
- string _filename:Excel 數(shù)據(jù)源文件路徑
- bool hastitle: 是否包含標(biāo)題,如果設(shè)置為true,則表示首行數(shù)據(jù)為列名稱定義
- string startaddress:可指定有效的起始單元格地址,不設(shè)置則默認(rèn)為“A1”(即第一個單元格)
- string endaddress:可指定有效的截止單元格地址,不設(shè)置則默認(rèn)為最后一個有值單元格(即XlCellType.xlCellTypeLastCell 枚舉)
通過3、4參數(shù)的定義,可以定義出有效的導(dǎo)入矩形區(qū)域。
打開數(shù)據(jù)源并計(jì)算Sheets
object[] rv=new object[2];
rv[0]=null;
rv[1]="";
//創(chuàng)建一個名為ExcelApp的組件對象
// ExcelApplication excel = new ExcelApplication();
Excel.Application excel = new Excel.Application();
excel.DisplayAlerts=false;
excel.AskToUpdateLinks=false;
Excel.Workbook xb=excel.Workbooks.Add(_filename);
//獲取活動的 worksheet和 excel sheet的個數(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();
//處理每一個sheet.....
}拆分合并的單元格
在獲取有效的單元格區(qū)域后,就開始遍歷單元格對象,判斷單元格對象 MergeCells 屬性即可,判斷 Cell.MergeCells.ToString() == "True" 即表示該單元格為合并單元格對象。
示例代碼如下:
//獲取起始單元和截止單元格,以確定有效區(qū)域
Excel.Range _startcell=worksheet.Range["A1","A1"]; //默認(rèn)為第一個單元格
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)獲取有值的最后一個有效的單元格
excel.Cells[1, _lastcell.Column + 1] = "vcol"+ (_lastcell.Column + 1).ToString();
_lastcell = worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);
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; //將拆分的單元格重新賦值(備份值)
}
}程序中通過 excel.Cells[1, _lastcell.Column + 1] = "vcol"+ (_lastcell.Column + 1).ToString(); 設(shè)置增加虛擬列列名,以達(dá)到重新計(jì)算最后單元格的目的。
創(chuàng)建DataTable
如果首行是列數(shù)據(jù),則以該行的值創(chuàng)建表結(jié)構(gòu),否則自動創(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開頭加序號
DataColumn dc=dt.Columns.Add();
dc.ColumnName=_colname;
dc.DataType=System.Type.GetType("System.String");
dc.AllowDBNull=true;
}
將單元格數(shù)據(jù)寫入DataTable
object[,] cells=null; 定義二維對象數(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);
}
刪除虛擬列
重新計(jì)算最后單元格,刪除 DataSet 最后列(虛擬列),這樣就達(dá)到正確輸出數(shù)據(jù)集數(shù)據(jù)的目的。 代碼如下:
_lastcell = excel.Cells[_lastcell.Row, _lastcell.Column - 1]; dt.Columns.Remove(dt.Columns[dt.Columns.Count - 1]);
總結(jié)
在實(shí)際的應(yīng)用中,還可能遇到更多的合并情況,我們要進(jìn)行進(jìn)一步的情況判斷和功能完善,讓導(dǎo)入功能變得更強(qiáng)大,本文示例提供了一些操作Excel相關(guān)的關(guān)鍵方法和屬性
到此這篇關(guān)于C#實(shí)現(xiàn)完善Excel不規(guī)則合并單元格數(shù)據(jù)導(dǎo)入的示例代碼的文章就介紹到這了,更多相關(guān)C# Excel 不規(guī)則合并單元格內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#?守護(hù)進(jìn)程的介紹及實(shí)現(xiàn)詳解
本文主要介紹了C#?守護(hù)進(jìn)程的介紹及實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
C#實(shí)現(xiàn)下載網(wǎng)頁HTML源碼的方法
這篇文章主要介紹了C#實(shí)現(xiàn)下載網(wǎng)頁HTML源碼的方法,是一個非常實(shí)用的技巧,還包含了對于下載失敗的判斷等邏輯處理,需要的朋友可以參考下2014-09-09
C# wpf 實(shí)現(xiàn)窗口任意區(qū)域點(diǎn)擊拖動
在wpf要實(shí)現(xiàn)此功能簡單形式還是比較容易的,但是有一些細(xì)節(jié)需要專門處理,比如與按鈕的點(diǎn)擊事件沖突問題,解決事件沖突問題后拖動的靈敏度,可復(fù)用性等,這篇文章主要介紹了C# wpf 實(shí)現(xiàn)窗口任意區(qū)域點(diǎn)擊拖動,需要的朋友可以參考下2024-03-03
unity中實(shí)現(xiàn)Edge瀏覽器鼠標(biāo)手勢的功能思路詳解
這篇文章主要介紹了unity中實(shí)現(xiàn)Edge瀏覽器鼠標(biāo)手勢的功能思路詳解,實(shí)現(xiàn)起來其實(shí)并不復(fù)雜,涉及的技術(shù)點(diǎn)有pc端和移動端屏幕拖動事件,二維向量的相關(guān)運(yùn)算,手勢匹配算法,事件系統(tǒng)設(shè)計(jì)模式,需要的朋友可以參考下2023-12-12
C# lambda表達(dá)式應(yīng)用如何找出元素在list中的索引
這篇文章主要介紹了C# lambda表達(dá)式應(yīng)用如何找出元素在list中的索引的相關(guān)資料,需要的朋友可以參考下2018-01-01
UnityWebRequest前后端交互實(shí)現(xiàn)過程解析
這篇文章主要介紹了UnityWebRequest前后端交互實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
C#實(shí)現(xiàn)將窗體固定在顯示器的左上角且不能移動的方法
這篇文章主要介紹了C#實(shí)現(xiàn)將窗體固定在顯示器的左上角且不能移動的方法,涉及C#窗體固定操作的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08
C# Winform實(shí)現(xiàn)自定義分頁控件
一些第三方的分頁控件要么就是界面不夠美觀大方,要么就是使用起來感覺很麻煩,所以本文就為大家介紹一下如何利用Winform自定義分頁控件,需要的可以參考一下2023-07-07

