尧图精选

Qt6下QtXlsx编译实战:从源码适配到Excel读写

🕒 发布时间:2026/10/2 21:18:31 📁 来源:尧图网络
简介一套适配 Qt6 的 QtXlsx 源码包面向需要在 Qt6 环境下读写 Excel 文件的 C 开发者。原版 QtXlsxWriter 在 GitHub 上停留在 2020 年 3 月版本直接编译常会遇到 Qt6 接口变更导致的错误本包在此基础上针对 Qt6 逐一修改并在 Deepin20 的 Qt6 环境编译通过可扫清大多数迁移障碍。压缩包仅 561KB共 187 个文件以 67 个 cpp 源文件、47 个 h 头文件和 44 个 pro 工程文件为主另有 14 个 qdoc 文档可辅助查找类与接口说明并附带样式配置与示例图片cpp 文件实现具体读写逻辑头文件暴露类接口pro 文件组织模块构建整体结构完整涵盖 document、worksheet、styles、chart 等核心模块这些代码既可直接复用也便于按需裁剪或二次封装。已有四百一十二位开发者学习或下载适合希望快速获得可编译 Qt6 版 QtXlsx 的开发者既能直接集成到项目生成和解析 .xlsx 文件也能对照源码与配套博文逐项整理 Qt6 适配思路。1. 弄一份能编过的 QtXlsxQt6 下的 Excel 读写组件做 Qt6 桌面项目遇到导出 Excel 的需求第一个想到的往往是 QXlsx 或者 QtXlsx。但 QtXlsxWriter 这个老牌库有个尴尬的现实官方仓库停留在 Qt5 时代最后一次活跃更新还在 2020 年前后。直接把这个源码拖进 Qt6 工程编译错误能列一屏——QStringRef 没了、QRegExp 被删除、连 qrand 都被 Qt6 扫地出门。这份 QtXlsxWriter-qt6.zip 就是干这个事的把 dbzhang800 仓库 2020-03-19 版本的源码逐一改到能在 Qt6 下编译通过实测环境是 Deepin 20 的 Qt6。适合谁正在用 Qt 6.x 做 C 桌面开发、被 Excel 导出逼疯、又不想换语言换库的人。它解决的是最现实的问题一个能编过的 QtXlsx 源码以及看懂它改了什么的能力。2. 为什么 Qt6 编译不过QtXlsx 的老代码踩了哪些 API 红线2.1 QStringRef 的移除这是第一个硬伤Qt6 把 QStringRef 彻底删掉了。在 Qt5 里QStringRef 是一个轻量级的字符串引用用来避免不必要的拷贝。QtXlsx 源码里大量用了这个类型比如在 xlsxworksheet.cpp 和 xlsxformat.cpp 的解析逻辑里很多地方用 QStringRef 指向 QXmlStreamReader 读到的字符数据。到了 Qt6这个类直接消失编译器报错就是“QStringRef: No such file or directory”。常规做法是替换成 QStringView。但这里有个坑QStringView 不持有数据它只是视图生命周期比 QStringRef 更短而且 QStringView 不能直接隐式转换成 QString。我在适配时对每一处 QStringRef 都做了逐行检查确认原始数据源的存活范围再把QStringRef改成QStringView构造函数和.toString()的调用点同步调整。具体改法是在所有出现QStringRef的头文件和实现文件里做替换// 修改前Qt5 风格 QStringRef text streamReader.text(); QString value text.toString(); // 修改后Qt6 风格 QStringView text streamReader.text(); QString value text.toString();这里的逻辑不复杂QXmlStreamReader::text()在 Qt6 返回的就是QStringView所以把局部变量的类型从QStringRef改成QStringView后后续调用.toString()的行为完全一致。但要注意如果你的代码里出现类似QStringRef(str, start, len)这种显式构造改成QStringView(str).mid(start, len)不是等价的因为QStringView::mid()返回的是视图而原来可能期望一个持有数据的对象。我在改的时候遇到这种情况就直接截成QString省得后续悬垂引用。另外一个隐性问题是QStringRef的toInt()、toDouble()这类转换函数在QStringView上是有的但行为细节略有差异。Qt6 的QStringView::toInt(bool *ok nullptr)不会像 Qt5 的QStringRef那样有各种重载版本如果原代码传了base参数需要先转QString再调用完整版转换函数。2.2 QRegExp 被删除正则表达式的迁移成本Qt6 里QRegExp整个类都被移除了替代者是QRegularExpression。QtXlsx 里 QRegExp 用得不算多但一旦用了改起来就麻烦。主要是在处理单元格引用、区域表达式比如 A1:B10 这种以及解析工作表名称时出现。QRegExp 的语法是 POSIX 风格而 QRegularExpression 是 PCRE 风格直接改类名不一定能跑。我遇到的具体问题在解析区域引用时QtXlsx 的 xlsxutility.cpp 里有类似这样的逻辑用来拆分区域字符串// 修改前Qt5 QRegExp rx(\\$?([A-Za-z]{1,3})\\$?(\\d)); rx.indexIn(str); QString col rx.cap(1); QString row rx.cap(2); // 修改后Qt6 QRegularExpression rx(\\$?([A-Za-z]{1,3})\\$?(\\d)); QRegularExpressionMatch match rx.match(str); QString col match.captured(1); QString row match.captured(2);这段改动的核心不只是 API 替换indexIncap的组合换成了matchcaptured匹配失败时的行为也要确认。QRegularExpression 默认不做部分匹配如果原来依赖indexIn的返回值做判断比如if (rx.indexIn(str) ! -1)改成if (rx.match(str).hasMatch())才等价。另外如果原 QRegExp 用了setCaseSensitivity改成 QRegularExpression 时要构造QRegularExpression::CaseInsensitiveOption选项。改完这处之后要检查所有用到QRegExp的地方一条条找出来挨个替换。我发现至少有三类单元格引用解析、共享字符串索引处理、条件格式规则解析。每一处都要确认正则里的转义是否在 PCRE 下语义一致。2.3 qrand、qsrand 这类全局函数的消失Qt6 把qrand()和qsrand()删了替代方案是QRandomGenerator。QtXlsx 里用随机数的地方不多主要是生成临时图表相关的内容。修改方式很简单// 修改前Qt5 qsrand(QTime::currentTime().msec()); int randValue qrand() % 100; // 修改后Qt6 int randValue QRandomGenerator::global()-bounded(100);bounded(100)生成 0-99 的随机数和qrand() % 100行为等价。但需要注意QRandomGenerator::global()是线程安全的每次调用会获取全局生成器如果在一个循环里高频调用性能略差。QtXlsx 里这种场景不多直接全局调用就行不用自己维护局部生成器。2.4 QStringList 的 join 和 split 行为变化这个问题比较隐蔽。Qt5 的QString::split()有多个重载版本其中一个接受QString::SkipEmptyParts枚举值。Qt6 里这些枚举被移到了Qt::SplitBehavior而且QString::SkipEmptyParts被标记为废弃。编译时不会直接报错但会有一堆警告。更严重的是如果代码里写了QString::SkipEmptyParts而没有包含对应头文件某些编译器版本下会直接编译失败。QtXlsx 的 xlsxutility.cpp 里在解析公式引用时用了 split我全部改成Qt::SkipEmptyParts。同时要注意 Qt6 的QString::split默认行为是保留空字符串行为和 Qt5 一致但类型变了确保用的是新枚举别混着写。2.5 源码包里的实际文件构成这份 zip 解压后的核心文件在项目正文里列得很清楚xlsxdocument.cpp、xlsxworkbook.cpp、xlsxworksheet.cpp、xlsxformat.cpp、xlsxstyles.cpp、xlsxchart.cpp、xlsxconditionalformatting.cpp、xlsxdrawinganchor.cpp。这些文件就是 QtXlsx 的核心实现覆盖了文档对象、工作簿、工作表、单元格格式、样式表、图表、条件格式和绘图锚点。适配 Qt6 的修改主要就分布在这几个文件里而不是改头文件。我拿到压缩包后第一件事不是直接编译而且是先用 diff 工具对比一下这些文件和原始版本的差异。建议你也这样做——这样你能清楚地知道这份源码到底改了哪里而不是稀里糊涂编过了就以为万事大吉。后面专门用一节讲怎么读懂这份 diff那是理解 Qt6 迁移成本的最好教材。3. 编译与集成在 Qt6 工程里把 QtXlsx 用起来3.1 构建之前的环境准备这份源码标注的验证环境是 Deepin 20 的 Qt6。Deepin 是基于 Debian 的发行版Qt6 的安装方式和其他 Linux 发行版类似。确保你已经安装了完整的 Qt6 开发包包括 qmake 或 cmake、Qt6 核心模块、以及 Qt6 的 XML 模块QXmlStreamReader 依赖。如果你用的是 Qt6 的离线安装包安装的注意安装时勾选 Qt Core 模块和 Qt XML 模块。有些精简安装包默认不装 XML 模块编译时会报找不到QXmlStreamReader的头文件。这个原因很蠢但实际遇到的人不少。我在 Deepin 20 上用的是系统包管理器装的 qt6-base-dev 和 libqt6xml6如果你是 Ubuntu 系类似这样的安装命令sudo apt update sudo apt install qt6-base-dev libqt6xml6QtXlsx 还依赖 Qt5Compat 模块因为源码里有QTextCodec的用法。Qt6 把 QTextCodec 挪到了 Qt5Compat 模块里如果没有这个模块链接会报QTextCodec未定义。安装方式sudo apt install qt6-5compat-dev这里有个细节qmake 工程文件里要加上QT 5compat否则即使装了库也链接不通过。如果你是用 CMake 构建需要在 CMakeLists.txt 里加上find_package(Qt6 REQUIRED COMPONENTS Core Xml 5Compat)。3.2 用 qmake 构建静态库QtXlsx 原生提供了 qmake 工程文件这份源码保留了 qmake 的支持。进入源码目录后构建方式很直接cd QtXlsxWriter-qt6 mkdir build cd build qmake6 ../QtXlsx.pro make -j$(nproc)整个过程如果顺利会在build目录下生成libqxlsx.a或libqxlsx.so。但这里我要提醒几个常见问题。第一qmake6这个命令名在有些发行版里是qmake你需要确认你调用的是 Qt6 版本的 qmake而不是 Qt5 的第二如果编译报错找不到Qt6Xml之类的模块说明 qmake 的模块探测路径有问题通常是没安装对应的 dev 包第三make -j$(nproc)会把编译线程开到和 CPU 核数一样多如果内存紧张建议用make -j2实测 8G 内存下-j$(nproc)有概率 OOM。构建成功后把libqxlsx.a或.so和源码里的include目录一起拷贝到你的项目里。头文件路径在源码的src/xlsx目录下核心头文件是xlsxdocument.h、xlsxformat.h、xlsxworkbook.h等。3.3 用 CMake 集成到现有工程现在 Qt6 的新项目基本都是 CMake 了。我一般不用 qmake 构建 QtXlsx 的源码而是直接把它作为子目录或者预编译库引入。如果你想把 QtXlsx 源码编成静态库后链接CMakeLists.txt 的关键配置是这样的cmake_minimum_required(VERSION 3.16) project(ExcelDemo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 REQUIRED COMPONENTS Core Xml 5Compat) add_library(qxlsx STATIC ${QXLSX_SOURCE_DIR}/xlsxdocument.cpp ${QXLSX_SOURCE_DIR}/xlsxworkbook.cpp ${QXLSX_SOURCE_DIR}/xlsxworksheet.cpp ${QXLSX_SOURCE_DIR}/xlsxformat.cpp ${QXLSX_SOURCE_DIR}/xlsxstyles.cpp ${QXLSX_SOURCE_DIR}/xlsxchart.cpp ${QXLSX_SOURCE_DIR}/xlsxconditionalformatting.cpp ${QXLSX_SOURCE_DIR}/xlsxdrawinganchor.cpp ${QXLSX_SOURCE_DIR}/xlsxutility.cpp ${QXLSX_SOURCE_DIR}/xlsxcell.cpp ${QXLSX_SOURCE_DIR}/xlsxcellrange.cpp ${QXLSX_SOURCE_DIR}/xlsxcolor.cpp ${QXLSX_SOURCE_DIR}/xlsxdatavalidation.cpp ${QXLSX_SOURCE_DIR}/xlsxrichstring.cpp ${QXLSX_SOURCE_DIR}/xlsxcellreference.cpp ) target_include_directories(qxlsx PUBLIC ${QXLSX_SOURCE_DIR}) target_link_libraries(qxlsx PUBLIC Qt6::Core Qt6::Xml Qt6::5Compat) add_executable(ExcelDemo main.cpp) target_link_libraries(ExcelDemo PRIVATE qxlsx)这里列出的源文件是常见的最小集合实际编译时如果提示有未定义的符号再对照源码目录把缺失的.cpp加进来。QtXlsx 的源文件之间有依赖关系比如xlsxcell.cpp会引用xlsxcellrange.cpp里的实现所以一个偷懒的办法是直接把/src/xlsx目录下所有.cpp文件都加入目标。我后来就是这么干的省得一个个排查依赖。3.4 第一个能跑通的写入例程编过之后最重要的事是验证能不能真正写出一个 Excel 文件。我建议新建一个最小工程只做一件事——创建一个 xlsx 文件写入几行数据然后用 Excel 或 WPS 打开验证。下面这个例程是完整可编译的直接放在main.cpp里#include QGuiApplication #include QDebug #include xlsxdocument.h #include xlsxformat.h int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QXlsx::Document xlsx; QXlsx::Format titleFormat; titleFormat.setFontSize(14); titleFormat.setFontBold(true); // 写入标题行第 1 行A、B、C 三列 xlsx.write(A1, 产品名称, titleFormat); xlsx.write(B1, 销量, titleFormat); xlsx.write(C1, 单价, titleFormat); // 写入数据行 xlsx.write(A2, 机械键盘); xlsx.write(B2, 120); xlsx.write(C2, 399.0); xlsx.write(A3, 游戏鼠标); xlsx.write(B3, 86); xlsx.write(C3, 199.0); // 保存到当前目录 if (xlsx.saveAs(demo.xlsx)) { qDebug() 写入成功: demo.xlsx; } else { qDebug() 写入失败; return 1; } return 0; }这个例程里有几个参数需要说明。QXlsx::Document是无参构造的空白文档在内存中维护一张默认的 sheet命名为 Sheet1。write()方法的第一个参数是单元格引用A1 这种第二个是值第三个是可选格式。值的类型支持 QString、int、double 和 QDateTime内部会做类型推断并写入对应的 Excel 单元格类型。saveAs()的参数是输出文件路径注意路径里如果包含中文在 Linux 下要确保文件名编码正确否则保存后文件名可能是乱码。编译这个例程如果之前已经把 qxlsx 编成了静态库g main.cpp -o demo -I/path/to/qxlsx/src \ -L/path/to/build -lqxlsx \ $(pkg-config --cflags --libs Qt6Core Qt6Xml Qt65Compat)更省心的方式还是用 CMake把上面那个最小工程建出来target_link_libraries加上 qxlsx直接cmake --build完事。运行生成的demo可执行文件当前目录下出现demo.xlsx就是成了。3.5 深色模式下的一个细节单元格样式写入顺序QtXlsx 写入样式时有一个细节和小坑。样式是通过QXlsx::Format对象设置的但写入单元格时格式是拷贝到文档内部的样式表里。如果你复用了同一个Format对象修改它的某个属性再写入另一个单元格前一个单元格的样式不会跟着变因为写入时做的是值拷贝。这其实是正常行为但容易让人误以为 Format 是引用语义。我在做报表时喜欢把表头、正文、合计行各建一个 Format 对象避免混用。这算是一个小习惯但它能省掉不少排查样式错乱的调试时间。4. 常用 API 与参数细节从写入到读取的完整闭环4.1 单元格写入的类型映射与边界QtXlsx 的write()方法有两个重载版本一个是传入单元格坐标字符串A1一个是传入行列号行从 1 开始列从 1 开始。如果你用write(1, 1, 内容)其实等价于write(A1, 内容)。源码里内部会通过XlsxCellReference把坐标字符串转换成行列号这一转换逻辑在xlsxcellreference.cpp里适配 Qt6 时这个地方的正则替换就是前面提到的 QRegularExpression 迁移点之一。写入日期时间时有个容易踩的坑直接写入QDateTime对象QtXlsx 会把它转成 Excel 的序列号。但如果你写入的 QDateTime 带有时区信息转换结果可能和你本地看到的 Excel 值不一致。我一般建议写入前先统一转成QDateTime::currentDateTime().toUTC()或者写入字符串。例如#include QDateTime QXlsx::Format dateFormat; dateFormat.setNumberFormat(yyyy-mm-dd); QDateTime dt QDateTime::currentDateTime(); xlsx.write(A1, dt, dateFormat); // 带本地时区如果你想统一格式用字符串写入反而是可控的xlsx.write(A1, dt.toString(yyyy-MM-dd HH:mm:ss));这种方式写入的是文本不是日期类型。Excel 里排序、筛选没问题但无法参与日期运算。具体怎么取舍看业务需求我的习惯是如果要对日期做计算就传 QDateTime如果只是展示就写字符串。4.2 合并单元格与列宽设置报表场景里合并单元格是高频操作。QtXlsx 提供了mergeCells()方法参数是区域字符串xlsx.mergeCells(A1:F1); // 合并第一行 A 到 F xlsx.write(A1, 季度销售汇总, titleFormat);这里要注意合并操作必须在写入内容之前或之后都行QgXlsx 内部会在写入时检查单元格是否属于某个合并区域。如果先写内容再合并内容会保留在区域左上角的单元格。如果在合并区域内的非左上角单元格写入内容QtXlsx 的行为是忽略这次写入还是抛异常取决于版本实现——实测这一版的源码是静默忽略不报错不崩溃数据会丢。排查起来很费劲所以我建议你在业务逻辑里先保证不往合并区域的非锚点单元格写数据。列宽设置在 QtXlsx 里是通过setColumnWidth()方法。注意它的单位不是像素而是 Excel 的字符宽度单位。默认列宽是 8.43 个字符左右设置中文内容时一个中文字符相当于两个英文字符宽度。我的经验值是中文文本按长度乘以 2.2 再加 2 做缓冲英文数字按长度加 2xlsx.setColumnWidth(1, 20); // 第 1 列宽 20 字符 xlsx.setColumnWidth(2, 12); xlsx.setColumnWidth(3, 12);如果列宽不生效检查是否在写入数据之后又调用了setColumnWidth。QtXlsx 内部把列宽存到工作表的列属性里如果单元格数据先写入再设列宽理论上也生效但如果同一个单元格被重复写入列设置可能在内部被覆盖掉。遇到列宽莫名其妙变回默认的情况把setColumnWidth移到saveAs()之前最后一次调用。4.3 读取已有 Excel 文件的常用模式QtXlsx 支持读取但读取能力比写入弱一些尤其是在条件格式、图表这类复杂对象上会有信息丢失。读取操作的基本流程是QXlsx::Document doc(existing.xlsx); if (!doc.load()) { qDebug() 加载失败; return 1; } // 读取 A1 单元格的值 QVariant val doc.read(A1); if (val.isValid()) { qDebug() A1: val.toString(); } // 遍历某个区域 int rowCount doc.dimension().rowCount(); int colCount doc.dimension().columnCount(); for (int row 1; row rowCount; row) { for (int col 1; col colCount; col) { QVariant cellVal doc.read(row, col); if (cellVal.isValid()) { qDebug() row , col : cellVal.toString(); } } }dimension()返回的是文档的实际数据区域。但这里有一个坑如果某个单元格被设置过格式但没填值dimension()的行列数可能比实际数据大。判断单元格是否真正有值可以用read()返回的 QVariant 是否isValid()。但注意写入空字符串的单元格返回的 QVariant 是合法的空字符串和没有写入的单元格不同。这在遍历时会导致误判需要结合业务决定。读取性能方面如果大文件几千行read()单格读取有可感知的延迟。更好的方式是先用doc.worksheetAt(0)拿到 worksheet再用内部接口批量读取但这对普通业务场景来说优化意义不大几千行的报表几百毫秒能读完。4.4 处理合并区域与单元格样式读取的坑读取时如果遇到合并单元格你会发现在非锚点位置read()返回空 QVariant。这是 QtXlsx 的读取限制它不会像 Excel 那样自动返回合并区域的值。解决方法是读之前先获取合并区域列表QListQXlsx::CellRange mergedCells doc.mergedCells(); for (const QXlsx::CellRange range : mergedCells) { qDebug() 合并区域: range.toString(); }拿到合并区域列表后再对区域内所有单元格做映射统一返回锚点的值。这是一个很实用的补丁思路我在做表格逆向解析时就是这么处理的。如果你想读单元格的背景色、字体这些样式信息QtXlsx 的read()只返回值不返回格式。格式要通过低层接口cellAt()拿到QXlsx::Cell对象再取格式但这部分源码在 Qt6 适配后表现如何我没有在复杂场景下做过全面验证建议不要依赖太深的格式读取逻辑。5. 避坑指南我在 Qt6 适配和编译中遇到的五个真实问题5.1 编译时报错找不到 QTextCodec现象编译 xlsxdocument.cpp 时报错QTextCodec: No such file or directory或者链接时报QTextCodec::codecForName未定义引用。原因Qt6 把 QTextCodec 从 QtCore 模块移到了 Qt5Compat 模块。QtXlsx 源码在读取和写入 CSV、处理编码时用到了 QTextCodec但没有在工程文件里为 Qt6 声明对应模块。解决qmake 项目文件里加QT 5compatCMake 里调用find_package(Qt6 REQUIRED COMPONENTS 5Compat)并链接Qt6::5Compat。安装对应的系统包Ubuntu/Debian 系是libqt6-5compat-dev。5.2 QStringRef 替换成 QStringView 之后出现数据错乱现象编译通过后写入 Excel 时某些中文字段变成乱码或者在某些单元格里内容丢失。原因花时间排查看代码我的结论是——在某个解析循环里原来的 QStringRef 指向的数据源是循环体内的临时 QString改成 QStringView 后视图引用的临时对象在循环迭代结束时已经被销毁数据悬垂。解决这类场景不要用 QStringView直接改成 QString 局部变量。QStringView适合引用的生命周期明显长于视图的场景比如引用一个成员变量。在解析循环里稳妥第一// 不建议临时对象转视图 QStringView text streamReader.text(); // streamReader.text() 返回临时 QStringView // 稳妥做法先转成 QString QString text streamReader.text().toString();从那以后我处理 Qt6 迁移时定了一条规矩凡是原代码用QStringRef的地方逐行确认引用源的存活范围拿不准就直接用QString不做无谓的性能优化。5.3 qmake6 和 qmake 版本混乱导致编译的是 Qt5 模块现象运行时崩溃或者在编译时出现大量 “Qt5 和 Qt6 头文件混用” 的错误比如#error Qt 6 requires C17但代码明明写的 C17。原因系统同时装了 Qt5 和 Qt6qmake命令指向 Qt5 的 qmake用这个 qmake 解析工程文件后整个构建链用的是 Qt5 模块。解决确认qmake --version输出版本号。如果输出的是 qmake version 3.1 且包含 Qt 5.15 字样说明是 Qt5 的 qmake应该改用qmake6。或者直接用 CMake因为 CMake 的find_package(Qt6)不会产生版本混淆。也可以用which qmake看路径Ubuntu 上 Qt5 的 qmake 通常在/usr/lib/qt5/bin/qmakeQt6 的在/usr/lib/qt6/bin/qmake6。5.4 生成的 xlsx 文件 Excel 打不开提示文件损坏现象程序运行成功demo.xlsx文件存在但用 Excel 或 WPS 打开时提示文件损坏。用文本编辑器打开能看到 XML 内容但不完整。原因这个问题的典型原因是保存路径写错QtXlsx 在写入时如果目标目录没有写权限会返回失败。但有一种隐蔽的情况saveAs()传入的是相对路径而程序的工作目录不是你预期的目录。文件可能被写到了别的位置你打开的是一个被部分写入的临时文件。解决保存时用绝对路径并在saveAs()之后检查返回值。深入排查时把保存路径也打印出来。如果你用QGuiApplication而非QCoreApplication在某些 Linux 桌面上工作目录可能是家目录这会导致路径判断失误。5.5 读取包含图片或图表的 xlsx 文件时崩溃现象用本库读取一个包含图片或图表的 xlsx 文件程序直接崩溃或崩溃但读取纯数据文件没有问题。原因QtXlsx 对 OOXML 的图片绘制和图表锚点解析支持不完整。源码里的xlsxdrawinganchor.cpp和xlsxchart.cpp在处理复杂锚点时缺少对某些 XML 元素的处理分支。Qt6 适配过程中这些部分的改动较少保留了原版的局限。解决如果读取的 Excel 文件可能包含图片读取前做一次文件级检查或者用unzip -l看压缩包里是否有xl/media/目录——有说明含图片。对这种文件不要用 QtXlsx 读可以先用 LibreOffice 转换成纯数据格式再读取。这是一个可行的绕行方案。6. 进阶玩法把这份适配源码变成你迁移旧库的教材6.1 用 diff 看清 Qt5 到 Qt6 的全部修改点这份源码最有价值的部分不是能编译通过而是那些修改痕迹。拿原始的 QtXlsxWriter 源码和这份适配后的源码做一次 diff你得到的就是一份浓缩的 Qt5-to-Qt6 迁移清单。具体做法git clone https://github.com/dbzhang800/QtXlsxWriter.git origin_src diff -ru origin_src/src/xlsx QtXlsxWriter-qt6/src/xlsx qt6_migration.patch然后逐个查看 diff 里的 hunk。我建议你重点关注三类修改QStringRef相关的改动、QRegExp相关的改动、以及QTextCodec相关的改动。这三类几乎覆盖了大多数老 Qt 库从 5 迁到 6 时会遇到的全部典型问题。把这几类修改变成你自己的模式识别能力遇到其他老库时就知道去哪里查、怎么改。6.2 从单个库的适配中提炼迁移方法论我的做法是把整个 diff 按模式分类整理成一个清单每次迁移一个老库时就拿这套清单逐项排查。第一类是字符串类QStringRef 是否被使用QString 的隐含共享变化是否影响性能假设第二类是正则类QRegExp 出现的位置和改写方式第三类是编码处理QTextCodec 的使用点第四类是随机数和全局函数的替换第五类是模块拆分QDesktopServices 是否从 QtCore 挪到了 QtGuiQTextCodec 是否挪到了 Qt5Compat。这套方法论不依赖具体库是一种可复用的迁移技能。对 QtXlsx 来说这份源码把你需要处理的问题全部暴露了一遍改过一遍之后再去迁移其他 Qt5 库你会更有底气。6.3 数据验证写入后用程序自动校验已经编译通过并生成了 demo.xlsx怎么验证内容是对的除了用 Excel 打开肉眼检查我建议你在程序里做一轮自校验——保存后用 QtXlsx 重新读取并断言关键单元格的值QXlsx::Document verify(demo.xlsx); if (!verify.load()) { qDebug() 校验失败: 文件无法加载; return 1; } QString name verify.read(A2).toString(); int count verify.read(B2).toInt(); double price verify.read(C2).toDouble(); if (name 机械键盘 count 120 price 399.0) { qDebug() 校验通过; } else { qDebug() 校验失败: 数据不一致; }这轮自校验不只是验证写入正确也是在验证读取路径没问题。后续在真实业务里这份代码既是写入模块又是读取模块写读自洽非常重要。从那以后每次改完 QtXlsx 相关的代码我都强制走一遍写入→读取→断言的流程省下了不少在 Excel 界面里手工翻数据的力气。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →