通用的將Excel匯入資料集的方法
阿新 • • 發佈:2019-02-08
開發中經常有需要將Excel匯入資料集的需要,但每張Excel的欄位都會不同,常規的做法有兩種:
一、針對每一張Excel的欄位與資料集欄位位置在程式中寫好,一一對應匯入
二、針對每一張Excel的欄位與資料集欄位配置好應用引數
這兩種方法都比較麻煩,很不靈活,所以我寫了一個比較通用的方法,只要符合以下條件就可以直接使用:
以下為delphi 程式碼:
一、針對每一張Excel的欄位與資料集欄位位置在程式中寫好,一一對應匯入
二、針對每一張Excel的欄位與資料集欄位配置好應用引數
這兩種方法都比較麻煩,很不靈活,所以我寫了一個比較通用的方法,只要符合以下條件就可以直接使用:
1、Excel第一行是欄位標題,第二行開始是資料
2、應用程式資料集顯示控制元件如(dbgrid,dxdbgrid等)名稱與Excel首行標題名一致(順序可以不同,數量也可以不同,如Excel的欄位:工號,姓名,年齡;顯示控制元件欄位:姓名,工號,年齡,建立時間;這也是可以的)
procedure ExportExcelToCDS(mygrid: TdxDBGrid; filename: string); var i,j,row,col,ValidFNCount:integer; MyExcel,Sheet:Variant; str1,Prompt,ts:string; fieldnames:array of string; fieldList:array of string; ColIndex:array of Integer;//Excel列序號 tmpcds:TDataSet; tmpds:TDataSource; CelValue:string; //搜尋Excel的標題是否有對應到資料表中的欄位 procedure SetFieldList; var t,t2,js:Integer; str1,str2:string; begin //搜尋Excel中的有效欄位 for t:=1 to col do begin str1:=StringReplace(Sheet.Cells[1,t].Text,' ','',[rfReplaceAll]); for t2:=0 to mygrid.ColumnCount-1 do begin str2:=StringReplace(mygrid.Columns[t2].Caption,' ','',[rfReplaceAll]); if str1=str2 then begin ValidFNCount:=ValidFNCount+1; Break; end; end; end; SetLength(fieldList,ValidFNCount); SetLength(ColIndex,ValidFNCount); js:=0; for t:=1 to col do begin str1:=StringReplace(Sheet.Cells[1,t].Text,' ','',[rfReplaceAll]); for t2:=0 to mygrid.ColumnCount-1 do begin str2:=StringReplace(mygrid.Columns[t2].Caption,' ','',[rfReplaceAll]); if str1=str2 then begin fieldList[js]:=mygrid.Columns[t2].FieldName;//欄位 fieldnames[js]:=mygrid.Columns[t2].Caption;//欄位顯示名稱 ColIndex[js]:=t;//Excel列序號1... js:=js+1; Break; end; end; end; end; function CheckField:string; var t:Integer; str1:string; begin for t:=1 to col do begin str1:=stringreplace(Sheet.Cells[1,t].Text,' ','',[rfReplaceAll]); if str1=fieldnames[i] then begin Break; end; Result:=str1; end; end; //Excel列名至少有一個與grid中的欄位相對應,是否不執行資料追加操作 function CheckFieldArray:Boolean; var t,t2:integer; begin t2:=0; for t:=0 to col-1 do begin if Trim(fieldList[t])<>'' then begin t2:=1; Break; end; end; if t2=0 then Result:=true else Result:=False; end; begin if UpperCase(ExtractFileExt(filename))<>uppercase('.xlsx') then begin ExportXLSToCDS(mygrid,filename); Exit; End; //支援Excel2007格式 tmpcds:=mygrid.DataSource.DataSet; tmpds:=mygrid.DataSource; try MyExcel:=CreateOleObject('Excel.Application'); except ts:='請安裝Excel'; MessageDlg(ts,mtWarning,[mbok],0); Exit; end; tmpcds.DisableControls; SetLength(fieldnames,mygrid.ColumnCount); try for i:=0 to mygrid.ColumnCount-1 do begin if mygrid.Columns[i].Visible then fieldnames[i]:=stringreplace(mygrid.Columns[i].Caption,' ','',[rfReplaceAll]); end; str1:=CheckField; if str1<>'' then begin MessageDlg('Excel中的'+str1+'不正確',mtError,[mbOK],0); Exit; end; MyExcel.Workbooks.open(filename); Sheet:=MyExcel.ActiveSheet; row:=Sheet.UsedRange.Rows.Count;//行數 col:=Sheet.UsedRange.Columns.Count;//列數 if row<=1 then begin Prompt:='Excel中至少有一條資料'+#13+'第一行是標題,其它行為資料行'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; if col<=1 then begin Prompt:='Excel中至少有一列資料'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; SetFieldList; if CheckFieldArray then begin Prompt:='Excel中第一行中的列名至少有一個與列表中欄位相同'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; Screen.Cursor:=crHourGlass; if not tmpcds.Active then tmpcds.Open; for i:=2 to row do begin Application.ProcessMessages; CelValue:=Trim(Sheet.Cells[i,0].Text); if (CelValue='') then Continue; tmpcds.Append; for j:=0 to ValidFNCount-1 do begin Application.ProcessMessages; CelValue:=Trim(Sheet.Cells[i,ColIndex[j]].Text); try //匯入的資料文字不可以有公式,否則會出錯 if (CelValue<>'') then begin case tmpcds.FieldByName(fieldList[j]).DataType of ftString: tmpcds.FieldByName(fieldList[j]).AsString:=CelValue; ftSmallint,ftInteger,ftWord,ftBoolean,ftFloat,ftCurrency, ftBCD,ftBytes: tmpcds.FieldByName(fieldList[j]).Value:=StrToFloat(CelValue); ftDate,ftTime,ftDateTime: tmpcds.FieldByName(fieldList[j]).AsDateTime:=Sheet.Cells[i,ColIndex[j]].Value; end; end; except on E:Exception do begin MessageDlg(E.Message+#13+'寫入欄位'+fieldList[j]+'時出錯,寫入內容:' +vartostr(CelValue)+#13+'Excel出錯行列:'+inttostr(i)+','+inttostr(j),mtError,[mbOK],0); end; end; end; tmpcds.Post; end; finally tmpcds.EnableControls; MyExcel.Workbooks.close; MyExcel.quit; Sheet:=Unassigned; MyExcel:=Unassigned; Screen.Cursor:=crDefault; MessageDlg('資料匯入完畢',mtInformation,[mbOK],0); end; end;
以上方法通過建立Excel物件匯入其資料的,未使用第三方控制元件,其傳入引數dxgrid可以改成您應用程式自已的控制元件型別,只要其帶有datasource.dataset屬性即可,比較靈活,但資料量比較大時可能比較慢,所以我做了一下改進,使用第三方控制元件讀取Excel,再執行匯入操作,這樣速度非常快,以下是我改進的使用TcxSpreadSheet控制元件讀取Excel的方法:
注:我使用的TcxSpreadSheet版本比較低,只支援.xls格式.各種開發工具使用以上方法只要將程式碼稍做改動即可。procedure ExportXLSToCDS(mygrid:TdxDBGrid;filename:string);//將excel匯入資料集 var i,j,row,col,ValidFNCount:integer; MyExcel:TcxSpreadSheet; str1,Prompt:string; fieldnames:array of string; fieldList:array of string; ColIndex:array of Integer;//Excel列序號 tmpcds:TDataSet; tmpds:TDataSource; CelValue:string; //搜尋Excel的標題是否有對應到資料表中的欄位 procedure SetFieldList; var t,t2,js:Integer; str1,str2:string; begin //搜尋Excel中的有效欄位 for t:=0 to col-1 do begin str1:=StringReplace(MyExcel.Sheet.getcellobject(t,0).Text,' ','',[rfReplaceAll]); for t2:=0 to mygrid.ColumnCount-1 do begin str2:=StringReplace(mygrid.Columns[t2].Caption,' ','',[rfReplaceAll]); if str1=str2 then begin ValidFNCount:=ValidFNCount+1; Break; end; end; end; SetLength(fieldList,ValidFNCount); SetLength(ColIndex,ValidFNCount); js:=0; for t:=0 to col-1 do begin str1:=StringReplace(MyExcel.Sheet.getcellobject(t,0).Text,' ','',[rfReplaceAll]); for t2:=0 to mygrid.ColumnCount-1 do begin str2:=StringReplace(mygrid.Columns[t2].Caption,' ','',[rfReplaceAll]); if str1=str2 then begin fieldList[js]:=mygrid.Columns[t2].FieldName;//欄位 fieldnames[js]:=mygrid.Columns[t2].Caption;//欄位顯示名稱 ColIndex[js]:=t;//Excel列序號1... js:=js+1; Break; end; end; end; end; function CheckField:string; var t:Integer; str1:string; begin for t:=0 to col-1 do begin str1:=stringreplace(MyExcel.Sheet.getcellobject(t,0).Text,' ','',[rfReplaceAll]); if str1=fieldnames[i] then begin Break; end; Result:=str1; end; end; //Excel列名至少有一個與grid中的欄位相對應,是否不執行資料追加操作 function CheckFieldArray:Boolean; var t,t2:integer; begin t2:=0; for t:=0 to col-1 do begin if Trim(fieldList[t])<>'' then begin t2:=1; Break; end; end; if t2=0 then Result:=true else Result:=False; end; begin tmpcds:=mygrid.DataSource.DataSet; tmpds:=mygrid.DataSource; MyExcel:=TcxSpreadSheet.Create(nil); tmpcds.DisableControls; SetLength(fieldnames,mygrid.ColumnCount); try for i:=0 to mygrid.ColumnCount-1 do begin if mygrid.Columns[i].Visible then fieldnames[i]:=stringreplace(mygrid.Columns[i].Caption,' ','',[rfReplaceAll]); end; str1:=CheckField; if str1<>'' then begin MessageDlg('Excel中的'+str1+'不正確',mtError,[mbOK],0); Exit; end; MyExcel.LoadFromFile(filename); row:=MyExcel.Sheet.ContentRowCount;//行數 col:=MyExcel.Sheet.ContentColCount;//列數 if row<=1 then begin Prompt:='Excel中至少有一條資料'+#13+'第一行是標題,其它行為資料行'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; if col<=1 then begin Prompt:='Excel中至少有一列資料'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; SetFieldList; if CheckFieldArray then begin Prompt:='Excel中第一行中的列名至少有一個與列表中欄位相同'+#13+'條件不符,操作取消'; MessageDlg(Prompt,mtWarning,[mbOK],0); Exit; end; Screen.Cursor:=crHourGlass; if not tmpcds.Active then tmpcds.Open; for i:=1 to row-1 do begin Application.ProcessMessages; CelValue:=Trim(MyExcel.Sheet.getcellobject(ColIndex[0],i).DisplayText); if (CelValue='') then Continue; tmpcds.Append; for j:=0 to ValidFNCount-1 do begin Application.ProcessMessages; CelValue:=MyExcel.Sheet.getcellobject(ColIndex[j],i).DisplayText; try //匯入的資料文字不可以有公式,否則會出錯 if VarToStr(CelValue)<>'' then begin case tmpcds.FieldByName(fieldList[j]).DataType of ftString: tmpcds.FieldByName(fieldList[j]).AsString:=CelValue; ftSmallint,ftInteger,ftWord,ftBoolean,ftFloat,ftCurrency, ftBCD,ftBytes: tmpcds.FieldByName(fieldList[j]).Value:=StrToFloat(CelValue); ftDate,ftTime,ftDateTime: tmpcds.FieldByName(fieldList[j]).AsDateTime:=MyExcel.Sheet.getcellobject(ColIndex[j],i).DateTime; end; end; except on E:Exception do begin MessageDlg(E.Message+#13+'寫入欄位'+fieldList[j]+'時出錯,寫入內容:' +vartostr(CelValue)+#13+'Excel出錯行列:'+inttostr(i)+','+inttostr(j),mtError,[mbOK],0); end; end; end; tmpcds.Post; end; finally tmpcds.EnableControls; Screen.Cursor:=crDefault; FreeAndNil(MyExcel); MessageDlg('資料匯入完畢',mtInformation,[mbOK],0); end; end;