用libxl读excel前须确认文件为真excel:.xlsx需microsoft生成且文件头为50 4b 03 04,.xls仅支持原生二进制;openxlsx不支持.xls,且默认不加载单元格值,需判类型再取值。

用 libxl 读 Excel 前先确认你拿的是不是「真 Excel」
很多用户一上来就报错:Invalid file format 或直接崩溃,根本没走到读取逻辑。原因往往是:你手里的 .xlsx 文件其实是 WPS 保存的“伪 xlsx”(底层是 zip + xml,但结构不标准),或是从网页导出的 .xls 实际是 HTML 表格伪装的。libxl 只支持原生二进制 .xls(Excel 97–2003)和部分兼容的 .xlsx(需 Microsoft Excel 2007+ 生成)。OpenXLSX 则**完全不支持 .xls**,只处理标准 OOXML 格式。
实操建议:
- 用
file your.xlsx(Linux/macOS)或 PowerShell 中Get-Item your.xlsx | Get-Content -Encoding Byte -TotalCount 8 | ForEach-Object { $_.ToString("X2") }检查文件头 —— 真正的.xlsx开头应为50 4B 03 04(即 PK..) - 用 Excel 重新「另存为」一次,格式选「Excel Workbook (*.xlsx)」,不要选「Excel 97–2003 Workbook」或「严格 Open XML」
- libxl 的免费版(
libxl.dll/.so)不支持密码保护、公式计算、条件格式;商用才解锁 —— 别在 demo 阶段误以为是代码问题
OpenXLSX 读取时为什么 worksheet.cell("A1").value() 返回空?
OpenXLSX 默认不加载单元格值,只解析结构。它把数值、字符串、公式、空单元格统一抽象为 XLCell,但 .value() 只对「已显式存储内容」的单元格有效。常见空返回场景:单元格是公式但没预计算、是空样式占位符、或被设置为「null value」(如导出工具写入的空字符串但标记为类型 XLValueType::empty)。
实操建议:
- 先用
cell.valueType()判断类型:XLValueType::empty、XLValueType::string、XLValueType::number等,再决定怎么取值 - 公式单元格需手动触发计算:
workbook.calculateFormulae()(仅限简单公式,不支持宏或外部引用) - 读字符串推荐用
cell.value<:string>()</:string>而非cell.value(),后者返回std::any,类型擦除后容易丢精度或抛bad_any_cast - 别依赖
worksheet.dimension()获取最大行列 —— 它返回的是「已使用区域」,但 Excel 允许中间留空行/列;稳妥做法是遍历worksheet.rows()或按业务逻辑限定范围
libxl 里 sheet->readStr() 和 sheet->readNum() 怎么选?
libxl 不做类型自动推断。同一个单元格,用错读取函数会返回 0 或空指针,且不报错。比如一个显示为 "123" 的文本型数字,用 readNum() 得到 0;而真实数字 123.45 用 readStr() 会得到 "123.45"(注意小数点),但若单元格格式设为「整数」,可能返回 "123" —— 这取决于 Excel 内部存储方式,不是显示格式。
实操建议:
- 优先用
sheet->cellType(row, col)获取类型:XL_CELL_NUMBER、XL_CELL_STRING、XL_CELL_BOOLEAN、XL_CELL_BLANK - 对疑似数字文本(如身份证、订单号),一律用
readStr(),避免截断或科学计数法变形(例如"12345678901234567890"用readNum()会变成1.23457e+19) -
readNum()返回double,但 Excel 存储精度只有 15 位有效数字 —— 别指望它还原完整 18 位银行卡号 - 中文路径或含 Unicode 表名需用 UTF-8 编码传入
book->getSheet(),否则返回nullptr(libxl Windows 版对宽字符支持弱)
两个库都绕不开的内存与异常细节
OpenXLSX 使用 RAII,但 XLDocument 构造失败(如文件损坏)不会抛异常,默认静默失败;libxl 则全靠返回值和 book->errorCode()。两者都不自动释放工作表句柄,重复打开同一文件不报错,但可能读到旧缓存。
实操建议:
- OpenXLSX 必须检查
doc.open("a.xlsx") == XLDocument::ErrorCode::Success,不能只看构造是否完成 - libxl 中
Book*创建后,必须配对调用book->release(),否则 Windows 下 DLL 句柄泄漏,多次运行后报ERROR_TOO_MANY_OPEN_FILES - 多线程读同一文件?OpenXLSX 不是线程安全的 —— 每个线程必须独立
XLDocument实例;libxl 的Book*实例也不可跨线程共享 - 大文件(>10MB)慎用 OpenXLSX 的
doc.saveAs(),它会全量重写 ZIP —— 如只需读,用doc.load()后别调saveAs
最常被跳过的一步:关掉杀毒软件实时扫描。libxl 和 OpenXLSX 在加载瞬间会高频访问 ZIP 内部文件,某些国产杀软会拦截并伪造空文件流,导致读出来全是空值 —— 换台干净机器试一次,往往就定位了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











