C++ 多執行緒ole讀取Excel
阿新 • • 發佈:2019-02-13
/****************************************************************************************** 這個類是從網上下載的,感謝原來的作者,我只試試是稍稍做了一下修正。 修正包括一些引數的使用不謹慎,bool 改為BOOL等,對於物件關係,我改了一部分,感覺原來的作者對於OO的思路部分不是很清楚。 對於這類東西OLE,我完全不瞭解,用別人封裝的東西感覺還是放心了很多,C++,偉大的C++ http://blog.csdn.net/gyssoft/archive/2007/04/29/1592104.aspx OLE讀寫EXCEL都比較慢,所以應該儘量減少OLE的次數 對於讀取,還有解決方法,請試用一下預載入的方式,這個方法一次載入所有的讀取資料,如此速度就飛快了。 據說寫資料是沒有什麼方法加快的 http://topic.csdn.net/t/20030626/21/1962211.html 增加了一些寫入方式的程式碼,保證可以寫入EXCEL資料區,但是對於儲存,我發現如果呼叫CLOSE並且儲存的方式, 速度非常慢,我不理解為什麼。 所以我吧EXCEL打開了,讓你進行後續管理, 修改了部分程式碼以使其支援多執行緒讀取Excel ******************************************************************************************/ //-----------------------excelfile.cpp---------------- #include "StdAfx.h" #include "ExcelTool.h" COleVariant covTrue((short)TRUE), covFalse((short)FALSE), covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); // CApplication CExcelTool::excel_application_; STR_excelData *stu_excelData=NULL; CExcelTool::CExcelTool(): already_preload_(FALSE) { } CExcelTool::~CExcelTool() { // CloseExcelFile(); ReleaseExcel(); } //初始化EXCEL檔案, BOOL CExcelTool::InitExcel() { // if (excel_application_) //因為excel_application_是一個全域性物件,因此線上程裡需要將其ReleaseDispatch // excel_application_.ReleaseDispatch(); //建立Excel 2000伺服器(啟動Excel if (!excel_application_.CreateDispatch("Excel.Application",NULL)) { AfxMessageBox("建立Excel服務失敗,你可能沒有安裝EXCEL,請檢查!"); return FALSE; } excel_application_.put_DisplayAlerts(FALSE); return TRUE; } // void CExcelTool::ReleaseExcel() { excel_application_.Quit(); excel_application_.ReleaseDispatch(); excel_application_=NULL; } //開啟excel檔案 BOOL CExcelTool::OpenExcelFile(const char *file_name) { //先關閉 CloseExcelFile(); //利用模板檔案建立新文件 excel_books_.AttachDispatch(excel_application_.get_Workbooks(),true); LPDISPATCH lpDis = NULL; lpDis = excel_books_.Add(COleVariant(file_name)); if (lpDis) { excel_work_book_.AttachDispatch(lpDis); //得到Worksheets excel_sheets_.AttachDispatch(excel_work_book_.get_Worksheets(),true); //記錄開啟的檔名稱 open_excel_file_ = file_name; return TRUE; } return FALSE; } //關閉開啟的Excel 檔案,預設情況不儲存檔案 void CExcelTool::CloseExcelFile(BOOL if_save) { //如果已經開啟,關閉檔案 if (open_excel_file_.IsEmpty() == FALSE) { //如果儲存,交給使用者控制,讓使用者自己存,如果自己SAVE,會出現莫名的等待 if (if_save) { ShowInExcel(TRUE); } else { // excel_work_book_.Close(COleVariant(short(FALSE)),COleVariant(open_excel_file_),covOptional); excel_books_.Close(); } //開啟檔案的名稱清空 open_excel_file_.Empty(); } excel_sheets_.ReleaseDispatch(); excel_work_sheet_.ReleaseDispatch(); excel_current_range_.ReleaseDispatch(); excel_work_book_.ReleaseDispatch(); excel_books_.ReleaseDispatch(); } void CExcelTool::SaveasXSLFile(const CString &xls_file) { excel_work_book_.SaveAs(COleVariant(xls_file), covOptional, covOptional, covOptional, covOptional, covOptional, 0, covOptional, covOptional, covOptional, covOptional, covOptional); return; } int CExcelTool::GetSheetCount() { return excel_sheets_.get_Count(); } CString CExcelTool::GetSheetName(long table_index) { CWorksheet sheet; sheet.AttachDispatch(excel_sheets_.get_Item(COleVariant((long)table_index)),true); CString name = sheet.get_Name(); sheet.ReleaseDispatch(); return name; } //按照序號載入Sheet表格,可以提前載入所有的表格內部資料 BOOL CExcelTool::LoadSheet(long table_index,BOOL pre_load) { LPDISPATCH lpDis = NULL; excel_current_range_.ReleaseDispatch(); excel_work_sheet_.ReleaseDispatch(); lpDis = excel_sheets_.get_Item(COleVariant((long)table_index)); if (lpDis) { excel_work_sheet_.AttachDispatch(lpDis,true); excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true); } else { return FALSE; } already_preload_ = FALSE; //如果進行預先載入 if (pre_load) { PreLoadSheet(); already_preload_ = TRUE; } return TRUE; } //按照名稱載入Sheet表格,可以提前載入所有的表格內部資料 BOOL CExcelTool::LoadSheet(const char* sheet,BOOL pre_load) { LPDISPATCH lpDis = NULL; excel_current_range_.ReleaseDispatch(); excel_work_sheet_.ReleaseDispatch(); lpDis = excel_sheets_.get_Item(COleVariant(sheet)); if (lpDis) { excel_work_sheet_.AttachDispatch(lpDis,true); excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true); } else { return FALSE; } // already_preload_ = FALSE; //如果進行預先載入 if (pre_load) { already_preload_ = TRUE; PreLoadSheet(); } return TRUE; } //得到列的總數 int CExcelTool::GetColumnCount() { CRange range; CRange usedRange; usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true); range.AttachDispatch(usedRange.get_Columns(), true); int count = range.get_Count(); usedRange.ReleaseDispatch(); range.ReleaseDispatch(); return count; } //得到行的總數 int CExcelTool::GetRowCount() { CRange range; CRange usedRange; usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true); range.AttachDispatch(usedRange.get_Rows(), true); int count = range.get_Count(); usedRange.ReleaseDispatch(); range.ReleaseDispatch(); return count; } //得到行列數 void CExcelTool::GetRowColumnCount(int& iRowSize,int& iColumnSize) { CRange range; CRange usedRange; usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true); range.AttachDispatch(usedRange.get_Rows(), true); iRowSize = range.get_Count(); range.ReleaseDispatch(); range.AttachDispatch(usedRange.get_Columns(), true); iColumnSize = range.get_Count(); range.ReleaseDispatch(); usedRange.ReleaseDispatch(); stu_excelData->m_iRow=iRowSize; stu_excelData->m_iColumn=iColumnSize; } //檢查一個CELL是否是字串 BOOL CExcelTool::IsCellString(long irow, long icolumn) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true); COleVariant vResult =range.get_Value2(); //VT_BSTR標示字串 if(vResult.vt == VT_BSTR) { return TRUE; } return FALSE; } //檢查一個CELL是否是數值 BOOL CExcelTool::IsCellInt(long irow, long icolumn) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true); COleVariant vResult =range.get_Value2(); //好像一般都是VT_R8 if(vResult.vt == VT_INT || vResult.vt == VT_R8) { return TRUE; } return FALSE; } // CString CExcelTool::GetCellString(long irow, long icolumn) { COleVariant vResult ; CString str; //字串 if (already_preload_ == FALSE) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true); vResult =range.get_Value2(); range.ReleaseDispatch(); } //如果資料依據預先載入了 else { long read_address[2]; VARIANT val; read_address[0] = irow; read_address[1] = icolumn; ole_safe_array_.GetElement(read_address, &val); vResult = val; } if(vResult.vt == VT_BSTR) { str=vResult.bstrVal; } //整數 else if (vResult.vt==VT_INT) { str.Format("%d",vResult.pintVal); } //8位元組的數字 else if (vResult.vt==VT_R8) { str.Format("%0.0f",vResult.dblVal); } //時間格式 else if(vResult.vt==VT_DATE) { SYSTEMTIME st; VariantTimeToSystemTime(vResult.date, &st); CTime tm(st); str=tm.Format("%Y-%m-%d"); } //單元格空的 else if(vResult.vt==VT_EMPTY) { str=""; } return str; } double CExcelTool::GetCellDouble(long irow, long icolumn) { double rtn_value = 0; COleVariant vresult; //字串 if (already_preload_ == FALSE) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true); vresult =range.get_Value2(); range.ReleaseDispatch(); } //如果資料依據預先載入了 else { long read_address[2]; VARIANT val; read_address[0] = irow; read_address[1] = icolumn; ole_safe_array_.GetElement(read_address, &val); vresult = val; } if (vresult.vt==VT_R8) { rtn_value = vresult.dblVal; } return rtn_value; } //VT_R8 int CExcelTool::GetCellInt(long irow, long icolumn) { int num; COleVariant vresult; if (already_preload_ == FALSE) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true); vresult = range.get_Value2(); range.ReleaseDispatch(); } else { long read_address[2]; VARIANT val; read_address[0] = irow; read_address[1] = icolumn; ole_safe_array_.GetElement(read_address, &val); vresult = val; } // num = static_cast(vresult.dblVal); return num; } void CExcelTool::SetCellString(long irow, long icolumn,CString new_string) { COleVariant new_value(new_string); CRange start_range = excel_work_sheet_.get_Range(COleVariant("A1"),covOptional); CRange write_range = start_range.get_Offset(COleVariant((long)irow -1),COleVariant((long)icolumn -1) ); write_range.put_Value2(new_value); start_range.ReleaseDispatch(); write_range.ReleaseDispatch(); } void CExcelTool::SetCellInt(long irow, long icolumn,int new_int) { COleVariant new_value((long)new_int); CRange start_range = excel_work_sheet_.get_Range(COleVariant("A1"),covOptional); CRange write_range = start_range.get_Offset(COleVariant((long)irow -1),COleVariant((long)icolumn -1) ); write_range.put_Value2(new_value); start_range.ReleaseDispatch(); write_range.ReleaseDispatch(); } // void CExcelTool::ShowInExcel(BOOL bShow) { excel_application_.put_Visible(bShow); excel_application_.put_UserControl(bShow); } //返回開啟的EXCEL檔名稱 CString CExcelTool::GetOpenFileName() { return open_excel_file_; } //取得開啟sheet的名稱 CString CExcelTool::GetLoadSheetName() { return excel_work_sheet_.get_Name(); } //取得列的名稱,比如27->AA char *CExcelTool::GetColumnName(long icolumn) { static char column_name[64]; size_t str_len = 0; while(icolumn > 0) { int num_data = icolumn % 26; icolumn /= 26; if (num_data == 0) { num_data = 26; icolumn--; } column_name[str_len] = (char)((num_data-1) + 'A' ); str_len ++; } column_name[str_len] = '\0'; //反轉 _strrev(column_name); return column_name; } //預先載入 void CExcelTool::PreLoadSheet() { CRange used_range; used_range = excel_work_sheet_.get_UsedRange(); VARIANT ret_ary = used_range.get_Value2(); if (!(ret_ary.vt & VT_ARRAY)) { return; } // ole_safe_array_.Clear(); ole_safe_array_.Attach(ret_ary); } //讀取Excel裡內容,並儲存進全域性map,這裡採用的是2個執行緒,一個從頭到尾讀取,一個反著讀,一旦兩人相遇,即停止。 void CExcelTool::GetContent(long IN iStartRow,long IN iEndRow,std::map>& OUT excelContents) { std::map>* mapContents=&stu_excelData->m_mapContents; if (iEndRow>iStartRow) //正向讀取 { if (iEndRow>stu_excelData->m_iRow) iEndRow=stu_excelData->m_iRow; for (int i=iStartRow;i<=iEndRow;i++) { std::map>::iterator it=mapContents->find(i); if (it!=mapContents->end()) { return; } std::vector arrColumns; for (int j=1;j<=stu_excelData->m_iColumn;j++) { COleVariant vResult ; if (already_preload_ == FALSE) //使用多執行緒時,應該必須使用預載入 { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)i),COleVariant((long)j)).pdispVal, true); vResult =range.get_Value2(); range.ReleaseDispatch(); } //如果資料依據預先載入了 else { long read_address[2]; VARIANT val; read_address[0] = i; read_address[1] = j; ole_safe_array_.GetElement(read_address, &val); vResult = val; } arrColumns.push_back(vResult); } InsertExcelData(mapContents,i,arrColumns); } }else { if (iStartRow>stu_excelData->m_iRow) iStartRow=stu_excelData->m_iRow; for (int i=iStartRow;i>=iEndRow;i--) { std::map>::iterator it=mapContents->find(i); if (it!=mapContents->end()) { return; } std::vector arrColumns; for (int j=1;j<=stu_excelData->m_iColumn;j++) { COleVariant vResult ; if (already_preload_ == FALSE) { CRange range; range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)i),COleVariant((long)j)).pdispVal, true); vResult =range.get_Value2(); range.ReleaseDispatch(); } //如果資料依據預先載入了 else { long read_address[2]; VARIANT val; read_address[0] = i; read_address[1] = j; ole_safe_array_.GetElement(read_address, &val); vResult = val; } arrColumns.push_back(vResult); } InsertExcelData(mapContents,i,arrColumns); } } excelContents=*mapContents; } //寫入資料時需要互斥,不同同時寫入,以免報錯 void CExcelTool::InsertExcelData(std::map>*& mapData,int iRow, std::vector& arrColumnData) { stu_excelData->m_muMapData.lock(); mapData->insert(make_pair(iRow,arrColumnData)); stu_excelData->m_muMapData.unlock(); }