首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

MFC ODBC 讀寫 Excel 完整類庫實現(二):核心源碼深度解析

     編輯:關於C語言

項目背景

本類庫 CODBCExcel 封裝 MFC CDatabase/CRecordset,通過 ODBC 驅動實現對 Excel(.xls)的讀取、寫入、修改、工作表管理,無需安裝 Office,適合服務端自動化報表場景。

核心架構

  • CODBCExcel:管理數據庫連接、工作表集合(map<CString, CODBCExcelSheet*>)
  • CODBCExcelSheet:單工作表操作,內含 CRecordset、單元格緩存(vector<vector<CODBCExcelCell>>)、列標題、狀態標記
  • CODBCExcelCell:單元格值存儲與類型轉換

關鍵功能模塊解析

1. 驅動檢測與連接建立

CString CODBCExcel::GetExcelDriver() {
    TCHAR szBuf[2001]; WORD cbBufMax = 2000, cbBufOut;
    if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) return "";
    TCHAR* pszBuf = szBuf;
    do {
        if (_tcsstr(pszBuf, _T("Excel"))) return CString(pszBuf);
        pszBuf = _tcschr(pszBuf, _T('\0')) + 1;
    } while (pszBuf[1] != _T('\0'));
    return "";
}

bool CODBCExcel::Open(const CString& strFileName) {
    if (m_db.IsOpen()) return true;
    CString strDSN;
    strDSN.Format(_T("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s"),
                  GetExcelDriver(), strFileName, strFileName);
    return m_db.OpenEx(strDSN, CDatabase::noOdbcDialog) == TRUE;
}

關鍵點:FIRSTROWHASNAMES=1 將第一行作為列名;CREATE_DB 支持自動建文件。

2. 工作表獲取與緩存策略

CODBCExcelSheet* CODBCExcel::GetWorkSheet(const CString& strSheetName) {
    CString strSQL;
    strSQL.Format(_T("SELECT * FROM [%s$A1:IV65536]"), strSheetName); // 固定範圍
    auto iter = m_Sheets.find(strSheetName);
    CODBCExcelSheet* sheet = nullptr;
    bool bNew = false;
    if (iter != m_Sheets.end()) {
        sheet = iter->second;
        if (sheet->m_nFlag == CODBCExcelSheet::Add || sheet->m_nFlag == CODBCExcelSheet::Update)
            return sheet;  // 已在編輯狀態,直接返回
    } else {
        sheet = new CODBCExcelSheet(*this, strSheetName);
        bNew = true;
    }
    try {
        sheet->m_recordset.Open(CRecordset::forwardOnly, strSQL, CRecordset::readOnly);
    } catch(...) {
        if (sheet) sheet->m_recordset.Close();
        if (!bNew) return sheet;
        delete sheet; return nullptr;
    }
    sheet->Init();  // 讀取列標題、全部數據入緩存
    m_Sheets.insert({strSheetName, sheet});
    return sheet;
}

3. 數據類型智能轉換(解決 5454 vs 5454.0 問題)

void CODBCExcelCell::Set(const CDBVariant& dbVal) {
    switch(dbVal.m_dwType) {
        case DBVT_SINGLE: {
            long lVal = (long)dbVal.m_fltVal;
            if (dbVal.m_fltVal != (float)lVal)
                m_strVal.Format(_T("%f"), dbVal.m_fltVal);
            else
                m_strVal.Format(_T("%ld"), lVal);  // 整數不顯示 .0
        } break;
        case DBVT_DOUBLE: {
            long lVal = (long)dbVal.m_dblVal;
            if (dbVal.m_dblVal != (double)lVal)
                m_strVal.Format(_T("%f"), dbVal.m_dblVal);
            else
                m_strVal.Format(_T("%ld"), lVal);
        } break;
        // ... 其他類型 ...
    }
}

這是 2009-11-02 修復的核心邏輯:浮點數若實為整數,按整數格式化,完美解決 Excel 顯示一致性問題。

4. 保存策略:Drop-Create-Insert

bool CODBCExcelSheet::Save() {
    switch(m_nFlag) {
        case Update:
            // DROP TABLE → CREATE TABLE → INSERT 逐行
        case Add:
            // CREATE TABLE → INSERT 逐行
        case Delete:
            // DROP TABLE
    }
    return true;
}

注意:Excel ODBC 驅動不支持 UPDATE 語句,只能刪表重建,故大量數據寫入較慢,適合中小規模報表。

5. 單元格訪問與動態擴容

CODBCExcelCell* CODBCExcelSheet::Cell(UINT rowIndex, UINT colIndex) {
    if (rowIndex > 65535 || colIndex > 255) return nullptr;
    if (colIndex >= m_dwCols) return nullptr;
    if (rowIndex >= m_dwRows) {
        m_dwRows++;
        m_Cells.resize(m_dwRows);
        for (auto& row : m_Cells) row.resize(m_dwCols);
        // 設置 Parent 指針以便回寫標記 dirty
    }
    return &m_Cells[rowIndex][colIndex];
}

工程實踐建議

  1. 線程安全:類庫非線程安全,多線程需外部加鎖
  2. 性能優化:批量寫入時可手動拼接 SQL,減少 ExecuteSQL 調用開銷
  3. 版本兼容:IV65536 限制為 Excel 2003 行數上限,.xlsx 需換 ACE 驅動
  4. 異常處理:所有 DB 操作均用 try-catch(CDBException*),防止 MFC 異常拋出到調用層

總結

這套類庫展示了 MFC ODBC 封裝的最佳實踐:連接管理、記錄集緩存、類型智能轉換、工作表生命週期控制。雖然 Excel ODBC 有諸多限制(無 UPDATE、行數上限、性能一般),但在「無 Office 環境、服務端導出報表」場景下,它仍是輕量級、免依賴的首選方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved