一、开发环境配置
1.1 开发环境要求
操作系统:Windows 7/10/11
开发工具:Visual Studio 2015/2017/2019/2022
字符集:Unicode
平台:Win32/x64
1.2 第三方库选择
推荐使用 libxl 库(无需安装 Excel,直接读写 .xls/.xlsx 文件):
- 官网:https://www.libxl.com/
- 下载:libxl-4.0.4.0(稳定版本)
- 优点:无需安装 Office,读写速度快,支持 Unicode
二、项目配置
2.1 Visual Studio 项目配置
(1)包含目录设置
项目属性 → C/C++ → 常规 → 附加包含目录
添加:D:\libxl-4.0.4.0\include_cpp
(2)库目录设置
项目属性 → 链接器 → 常规 → 附加库目录
添加:D:\libxl-4.0.4.0\lib\vc2015
(3)链接库设置
项目属性 → 链接器 → 输入 → 附加依赖项
添加:libxl.lib
(4)复制 DLL 文件
将 libxl.dll 复制到:
• 项目目录\Debug\
• 项目目录\Release\
• 或 System32/SysWOW64(系统级)
三、源码实现
3.1 Excel 操作封装类 (ExcelHelper.h)
cpp
#ifndef __EXCEL_HELPER_H__
#define __EXCEL_HELPER_H__
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include "libxl.h"
#pragma comment(lib, "libxl.lib")
class ExcelHelper
{
public:
ExcelHelper();
~ExcelHelper();
// 文件操作
bool CreateNewFile(const std::wstring& filePath);
bool OpenExistingFile(const std::wstring& filePath);
bool SaveFile();
bool SaveAsFile(const std::wstring& filePath);
void CloseFile();
// Sheet 操作
int AddSheet(const std::wstring& sheetName);
bool SelectSheet(int sheetIndex);
bool DeleteSheet(int sheetIndex);
int GetSheetCount();
std::wstring GetSheetName(int sheetIndex);
// 单元格读写
bool WriteCellString(int row, int col, const std::wstring& value);
bool WriteCellNumber(int row, int col, double value);
bool WriteCellInteger(int row, int col, int value);
bool WriteCellFormula(int row, int col, const std::wstring& formula);
std::wstring ReadCellString(int row, int col);
double ReadCellNumber(int row, int col);
int ReadCellInteger(int row, int col);
// 批量操作
bool WriteRow(int row, const std::vector<std::wstring>& data);
bool WriteColumn(int col, const std::vector<std::wstring>& data);
bool ReadRow(int row, std::vector<std::wstring>& data);
bool ReadColumn(int col, std::vector<std::wstring>& data);
// 格式设置
void SetCellFont(int row, int col, const std::wstring& fontName, int fontSize, bool bold = false);
void SetCellAlignment(int row, int col, libxl::AlignH alignH, libxl::AlignV alignV);
void SetCellBorder(int row, int col, libxl::BorderStyle style);
void SetCellBackgroundColor(int row, int col, libxl::Color color);
void AutoFitColumn(int col);
// 行列操作
void SetColumnWidth(int col, double width);
void SetRowHeight(int row, double height);
void MergeCells(int row1, int col1, int row2, int col2);
// 数据验证
bool IsCellEmpty(int row, int col);
int FindRowByValue(int col, const std::wstring& value);
int FindColByValue(int row, const std::wstring& value);
private:
libxl::Book* m_book;
libxl::Sheet* m_sheet;
std::wstring m_filePath;
bool m_isNewFile;
};
#endif // __EXCEL_HELPER_H__
3.2 Excel 操作封装实现 (ExcelHelper.cpp)
cpp
#include "ExcelHelper.h"
#include <fstream>
#include <sstream>
ExcelHelper::ExcelHelper()
: m_book(nullptr)
, m_sheet(nullptr)
, m_isNewFile(false)
{
// 创建 Excel 工作簿对象
m_book = xlCreateBook();
if (m_book)
{
m_book->setKey(L"Your Name", L"Your License Key"); // 如果有许可证
}
}
ExcelHelper::~ExcelHelper()
{
CloseFile();
}
// 创建新文件
bool ExcelHelper::CreateNewFile(const std::wstring& filePath)
{
CloseFile();
if (!m_book)
{
m_book = xlCreateBook();
if (!m_book) return false;
}
m_filePath = filePath;
m_isNewFile = true;
return true;
}
// 打开已有文件
bool ExcelHelper::OpenExistingFile(const std::wstring& filePath)
{
CloseFile();
if (!m_book)
{
m_book = xlCreateBook();
if (!m_book) return false;
}
if (!m_book->load(filePath.c_str()))
{
std::wcerr << L"Failed to load file: " << filePath << std::endl;
return false;
}
m_filePath = filePath;
m_isNewFile = false;
return true;
}
// 保存文件
bool ExcelHelper::SaveFile()
{
if (!m_book || m_filePath.empty())
return false;
if (m_isNewFile)
{
return m_book->save(m_filePath.c_str());
}
else
{
return m_book->save(m_filePath.c_str());
}
}
// 另存为
bool ExcelHelper::SaveAsFile(const std::wstring& filePath)
{
if (!m_book)
return false;
return m_book->save(filePath.c_str());
}
// 关闭文件
void ExcelHelper::CloseFile()
{
if (m_book)
{
m_book->release();
m_book = nullptr;
}
m_sheet = nullptr;
m_filePath.clear();
}
// 添加工作表
int ExcelHelper::AddSheet(const std::wstring& sheetName)
{
if (!m_book)
return -1;
m_sheet = m_book->addSheet(sheetName.c_str());
if (m_sheet)
return m_book->sheetCount() - 1;
return -1;
}
// 选择工作表
bool ExcelHelper::SelectSheet(int sheetIndex)
{
if (!m_book || sheetIndex < 0 || sheetIndex >= m_book->sheetCount())
return false;
m_sheet = m_book->getSheet(sheetIndex);
return (m_sheet != nullptr);
}
// 获取工作表数量
int ExcelHelper::GetSheetCount()
{
return m_book ? m_book->sheetCount() : 0;
}
// 写入字符串
bool ExcelHelper::WriteCellString(int row, int col, const std::wstring& value)
{
if (!m_sheet)
return false;
return m_sheet->writeStr(row, col, value.c_str());
}
// 写入数字
bool ExcelHelper::WriteCellNumber(int row, int col, double value)
{
if (!m_sheet)
return false;
return m_sheet->writeNum(row, col, value);
}
// 写入整数
bool ExcelHelper::WriteCellInteger(int row, int col, int value)
{
return WriteCellNumber(row, col, static_cast<double>(value));
}
// 写入公式
bool ExcelHelper::WriteCellFormula(int row, int col, const std::wstring& formula)
{
if (!m_sheet)
return false;
return m_sheet->writeFormula(row, col, formula.c_str());
}
// 读取字符串
std::wstring ExcelHelper::ReadCellString(int row, int col)
{
if (!m_sheet)
return L"";
const wchar_t* value = m_sheet->readStr(row, col);
return value ? std::wstring(value) : L"";
}
// 读取数字
double ExcelHelper::ReadCellNumber(int row, int col)
{
if (!m_sheet)
return 0.0;
return m_sheet->readNum(row, col);
}
// 读取整数
int ExcelHelper::ReadCellInteger(int row, int col)
{
return static_cast<int>(ReadCellNumber(row, col));
}
// 写入整行
bool ExcelHelper::WriteRow(int row, const std::vector<std::wstring>& data)
{
if (!m_sheet)
return false;
for (size_t col = 0; col < data.size(); ++col)
{
if (!WriteCellString(row, static_cast<int>(col), data[col]))
return false;
}
return true;
}
// 写入整列
bool ExcelHelper::WriteColumn(int col, const std::vector<std::wstring>& data)
{
if (!m_sheet)
return false;
for (size_t row = 0; row < data.size(); ++row)
{
if (!WriteCellString(static_cast<int>(row), col, data[row]))
return false;
}
return true;
}
// 读取整行
bool ExcelHelper::ReadRow(int row, std::vector<std::wstring>& data)
{
if (!m_sheet)
return false;
data.clear();
int cols = 0;
// 查找最后一列
while (!IsCellEmpty(row, cols))
{
data.push_back(ReadCellString(row, cols));
cols++;
}
return true;
}
// 读取整列
bool ExcelHelper::ReadColumn(int col, std::vector<std::wstring>& data)
{
if (!m_sheet)
return false;
data.clear();
int rows = 0;
// 查找最后一行
while (!IsCellEmpty(rows, col))
{
data.push_back(ReadCellString(rows, col));
rows++;
}
return true;
}
// 设置字体
void ExcelHelper::SetCellFont(int row, int col, const std::wstring& fontName, int fontSize, bool bold)
{
if (!m_sheet)
return;
libxl::Format* format = m_book->addFormat();
if (format)
{
format->setFont(fontName.c_str());
format->setFontSize(fontSize);
format->setBold(bold);
m_sheet->setColFormat(col, col, format);
}
}
// 设置对齐方式
void ExcelHelper::SetCellAlignment(int row, int col, libxl::AlignH alignH, libxl::AlignV alignV)
{
if (!m_sheet)
return;
libxl::Format* format = m_book->addFormat();
if (format)
{
format->setAlignH(alignH);
format->setAlignV(alignV);
m_sheet->setColFormat(col, col, format);
}
}
// 设置边框
void ExcelHelper::SetCellBorder(int row, int col, libxl::BorderStyle style)
{
if (!m_sheet)
return;
libxl::Format* format = m_book->addFormat();
if (format)
{
format->setBorder(style);
m_sheet->setColFormat(col, col, format);
}
}
// 自动调整列宽
void ExcelHelper::AutoFitColumn(int col)
{
if (!m_sheet)
return;
m_sheet->setCol(col, col, -1); // -1 表示自动调整
}
// 设置列宽
void ExcelHelper::SetColumnWidth(int col, double width)
{
if (!m_sheet)
return;
m_sheet->setCol(col, col, width);
}
// 设置行高
void ExcelHelper::SetRowHeight(int row, double height)
{
if (!m_sheet)
return;
m_sheet->setRow(row, height);
}
// 合并单元格
void ExcelHelper::MergeCells(int row1, int col1, int row2, int col2)
{
if (!m_sheet)
return;
m_sheet->setMerge(row1, row2, col1, col2);
}
// 判断单元格是否为空
bool ExcelHelper::IsCellEmpty(int row, int col)
{
if (!m_sheet)
return true;
return m_sheet->isCellBlank(row, col);
}
// 查找行
int ExcelHelper::FindRowByValue(int col, const std::wstring& value)
{
if (!m_sheet)
return -1;
for (int row = 0; row < m_sheet->lastRow(); ++row)
{
if (ReadCellString(row, col) == value)
return row;
}
return -1;
}
// 查找列
int ExcelHelper::FindColByValue(int row, const std::wstring& value)
{
if (!m_sheet)
return -1;
for (int col = 0; col < m_sheet->lastCol(); ++col)
{
if (ReadCellString(row, col) == value)
return col;
}
return -1;
}
3.3 主程序示例 (main.cpp)
cpp
#include "ExcelHelper.h"
#include <iostream>
#include <vector>
int main()
{
std::cout << "=== Excel 文件读写测试 ===" << std::endl;
ExcelHelper excel;
// 1. 创建新文件
std::wstring filePath = L".\\TestData.xlsx";
if (!excel.CreateNewFile(filePath))
{
std::cerr << "创建 Excel 文件失败!" << std::endl;
return -1;
}
// 2. 添加工作表
int sheetIndex = excel.AddSheet(L"学生成绩表");
if (sheetIndex < 0)
{
std::cerr << "创建工作表失败!" << std::endl;
return -1;
}
// 3. 选择工作表
if (!excel.SelectSheet(sheetIndex))
{
std::cerr << "选择工作表失败!" << std::endl;
return -1;
}
// 4. 写入表头
std::vector<std::wstring> headers = {
L"学号", L"姓名", L"语文", L"数学", L"英语", L"总分"
};
if (!excel.WriteRow(0, headers))
{
std::cerr << "写入表头失败!" << std::endl;
return -1;
}
// 5. 写入数据
std::vector<std::vector<std::wstring>> students = {
{L"001", L"张三", L"85", L"92", L"88", L"265"},
{L"002", L"李四", L"78", L"96", L"82", L"256"},
{L"003", L"王五", L"92", L"88", L"91", L"271"},
{L"004", L"赵六", L"65", L"72", L"68", L"205"}
};
for (size_t i = 0; i < students.size(); ++i)
{
if (!excel.WriteRow(static_cast<int>(i + 1), students[i]))
{
std::cerr << "写入第 " << i + 1 << " 行失败!" << std::endl;
return -1;
}
}
// 6. 设置格式
excel.SetCellFont(0, 0, L"Arial", 12, true);
excel.SetCellAlignment(0, 0, libxl::ALIGNH_CENTER, libxl::ALIGNV_CENTER);
excel.SetCellBorder(0, 0, libxl::BORDERSTYLE_THIN);
excel.SetColumnWidth(0, 15);
excel.AutoFitColumn(1);
// 7. 合并单元格示例
excel.MergeCells(6, 0, 6, 2);
excel.WriteCellString(6, 0, L"班级平均分:");
// 8. 保存文件
if (!excel.SaveFile())
{
std::cerr << "保存文件失败!" << std::endl;
return -1;
}
std::wcout << L"Excel 文件创建成功:" << filePath << std::endl;
// 9. 读取测试
std::cout << "\n=== 读取 Excel 文件测试 ===" << std::endl;
if (!excel.OpenExistingFile(filePath))
{
std::cerr << "打开 Excel 文件失败!" << std::endl;
return -1;
}
if (!excel.SelectSheet(0))
{
std::cerr << "选择工作表失败!" << std::endl;
return -1;
}
// 读取表头
std::vector<std::wstring> readHeaders;
if (excel.ReadRow(0, readHeaders))
{
std::wcout << L"表头:";
for (const auto& header : readHeaders)
{
std::wcout << header << L" ";
}
std::wcout << std::endl;
}
// 读取第一行数据
std::vector<std::wstring> firstRow;
if (excel.ReadRow(1, firstRow))
{
std::wcout << L"第一行数据:";
for (const auto& cell : firstRow)
{
std::wcout << cell << L" ";
}
std::wcout << std::endl;
}
// 读取特定单元格
std::wstring name = excel.ReadCellString(1, 1);
double mathScore = excel.ReadCellNumber(1, 3);
std::wcout << L"姓名:" << name << L",数学成绩:" << mathScore << std::endl;
// 10. 查找测试
int rowIndex = excel.FindRowByValue(1, L"王五");
if (rowIndex >= 0)
{
std::wcout << L"找到王五在第 " << rowIndex + 1 << L" 行" << std::endl;
}
excel.CloseFile();
std::cout << "\nExcel 操作完成!" << std::endl;
return 0;
}
四、MFC 对话框应用示例
4.1 MFC 主对话框头文件 (ExcelDlg.h)
cpp
#pragma once
#include "ExcelHelper.h"
class CExcelDlg : public CDialogEx
{
public:
CExcelDlg(CWnd* pParent = nullptr);
protected:
virtual void DoDataExchange(CDataExchange* pDX);
virtual BOOL OnInitDialog();
// 按钮事件
afx_msg void OnBnClickedBtnCreate();
afx_msg void OnBnClickedBtnOpen();
afx_msg void OnBnClickedBtnSave();
afx_msg void OnBnClickedBtnRead();
afx_msg void OnBnClickedBtnWrite();
private:
ExcelHelper m_excel;
CString m_filePath;
CListCtrl m_listCtrl;
DECLARE_MESSAGE_MAP()
};
4.2 MFC 主对话框实现 (ExcelDlg.cpp)
cpp
#include "stdafx.h"
#include "ExcelDlg.h"
BEGIN_MESSAGE_MAP(CExcelDlg, CDialogEx)
ON_BN_CLICKED(IDC_BTN_CREATE, &CExcelDlg::OnBnClickedBtnCreate)
ON_BN_CLICKED(IDC_BTN_OPEN, &CExcelDlg::OnBnClickedBtnOpen)
ON_BN_CLICKED(IDC_BTN_SAVE, &CExcelDlg::OnBnClickedBtnSave)
ON_BN_CLICKED(IDC_BTN_READ, &CExcelDlg::OnBnClickedBtnRead)
ON_BN_CLICKED(IDC_BTN_WRITE, &CExcelDlg::OnBnClickedBtnWrite)
END_MESSAGE_MAP()
BOOL CExcelDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 初始化列表控件
m_listCtrl.SubclassDlgItem(IDC_LIST_DATA, this);
m_listCtrl.InsertColumn(0, _T("学号"), LVCFMT_LEFT, 80);
m_listCtrl.InsertColumn(1, _T("姓名"), LVCFMT_LEFT, 80);
m_listCtrl.InsertColumn(2, _T("语文"), LVCFMT_LEFT, 60);
m_listCtrl.InsertColumn(3, _T("数学"), LVCFMT_LEFT, 60);
m_listCtrl.InsertColumn(4, _T("英语"), LVCFMT_LEFT, 60);
return TRUE;
}
void CExcelDlg::OnBnClickedBtnCreate()
{
CFileDialog dlg(FALSE, _T(".xlsx"), NULL, OFN_OVERWRITEPROMPT,
_T("Excel Files (*.xlsx)|*.xlsx|All Files (*.*)|*.*||"));
if (dlg.DoModal() == IDOK)
{
m_filePath = dlg.GetPathName();
if (m_excel.CreateNewFile((LPCTSTR)m_filePath))
{
int sheetIdx = m_excel.AddSheet(_T("学生成绩"));
m_excel.SelectSheet(sheetIdx);
// 写入表头
std::vector<std::wstring> headers = {L"学号", L"姓名", L"语文", L"数学", L"英语"};
m_excel.WriteRow(0, headers);
MessageBox(_T("Excel 文件创建成功!"), _T("提示"), MB_ICONINFORMATION);
}
}
}
void CExcelDlg::OnBnClickedBtnOpen()
{
CFileDialog dlg(TRUE, _T(".xlsx"), NULL, OFN_FILEMUSTEXIST,
_T("Excel Files (*.xlsx)|*.xlsx|All Files (*.*)|*.*||"));
if (dlg.DoModal() == IDOK)
{
m_filePath = dlg.GetPathName();
if (m_excel.OpenExistingFile((LPCTSTR)m_filePath))
{
m_excel.SelectSheet(0);
MessageBox(_T("Excel 文件打开成功!"), _T("提示"), MB_ICONINFORMATION);
}
}
}
void CExcelDlg::OnBnClickedBtnSave()
{
if (m_excel.SaveFile())
{
MessageBox(_T("文件保存成功!"), _T("提示"), MB_ICONINFORMATION);
}
}
void CExcelDlg::OnBnClickedBtnWrite()
{
// 从界面获取数据并写入 Excel
CString studentId, name, chinese, math, english;
GetDlgItemText(IDC_EDIT_ID, studentId);
GetDlgItemText(IDC_EDIT_NAME, name);
GetDlgItemText(IDC_EDIT_CHINESE, chinese);
GetDlgItemText(IDC_EDIT_MATH, math);
GetDlgItemText(IDC_EDIT_ENGLISH, english);
int row = m_listCtrl.GetItemCount() + 1; // +1 跳过表头
m_excel.WriteCellString(row, 0, (LPCTSTR)studentId);
m_excel.WriteCellString(row, 1, (LPCTSTR)name);
m_excel.WriteCellString(row, 2, (LPCTSTR)chinese);
m_excel.WriteCellString(row, 3, (LPCTSTR)math);
m_excel.WriteCellString(row, 4, (LPCTSTR)english);
// 添加到列表控件
int nItem = m_listCtrl.InsertItem(row - 1, studentId);
m_listCtrl.SetItemText(nItem, 1, name);
m_listCtrl.SetItemText(nItem, 2, chinese);
m_listCtrl.SetItemText(nItem, 3, math);
m_listCtrl.SetItemText(nItem, 4, english);
MessageBox(_T("数据写入成功!"), _T("提示"), MB_ICONINFORMATION);
}
void CExcelDlg::OnBnClickedBtnRead()
{
m_listCtrl.DeleteAllItems();
std::vector<std::wstring> rowData;
int row = 1; // 从第1行开始(跳过表头)
while (m_excel.ReadRow(row, rowData) && !rowData.empty())
{
int nItem = m_listCtrl.InsertItem(row - 1, rowData[0].c_str());
for (size_t col = 1; col < rowData.size(); ++col)
{
m_listCtrl.SetItemText(nItem, col, rowData[col].c_str());
}
row++;
rowData.clear();
}
MessageBox(_T("数据读取成功!"), _T("提示"), MB_ICONINFORMATION);
}
参考代码 VC++读写Excel文件源代码 www.youwenfan.com/contentcsu/60836.html
五、编译与部署注意事项
5.1 常见编译错误解决
| 错误类型 | 解决方法 |
|---|---|
| 找不到 libxl.h | 检查附加包含目录是否正确 |
| 找不到 libxl.lib | 检查附加库目录和依赖项 |
| 无法打开 libxl.dll | 将 DLL 复制到可执行文件目录 |
| 中文乱码 | 确保项目使用 Unicode 字符集 |
| 内存泄漏 | 确保正确调用 CloseFile() |
5.2 替代方案
如果不想使用 libxl,可以考虑:
- ODBC:使用 Microsoft Jet 引擎(仅支持 .xls)
- ADO:ActiveX Data Objects(依赖 Office)
- CSV 格式:直接读写逗号分隔文件(最简单)