ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

统信UOS下QT5读写xls:解决中文路径问题的ExcelFormat修改版

统信UOS下QT5读写xls:解决中文路径问题的ExcelFormat修改版 简介这份资源面向在 Linux 环境下从事 Qt5 开发、需要处理 Excel 文件的工程师重点解决 xls 读写时中文路径与中文内容乱码的问题。其核心是经过修改的 ExcelFormat 第三方库源码在统信 UOS 系统的 aarch64、mips64 架构上经 gcc 编译验证通过可设置表格样式并附带可直接参考的使用示例。压缩包共 21 个文件约 2.61MB以 cpp、h 源码与编译产物 o 文件为主另含 pro 工程文件、ui 界面文件、Makefile 及库文件结构完整便于直接集成。作者定位到 wcstombs 与 mbstowcs 转换出错这一根因通过在 create、open 函数中按宏调整文件名长度计算并在头文件定义 NON_ENGLISH_PATH_FILENAME_SUPPORT使中文、日文、韩文路径均可正常读写。目前已有 392 人学习适合需要跨平台处理表格数据的开发者参考排错与二次开发。1. 统信 UOS 上跑通 QT5 读写 xls一个被中文路径卡了三天的真实场景在统信 UOS 的 aarch64 机器上部署一个 QT5 桌面工具功能很简单让用户选一个路径把界面上的数据导出成.xls再支持从.xls读回来。结果程序在英文路径下一切正常只要路径里出现一个中文字符open就返回失败日志里连个像样的错误都没有。排查到后面才发现问题不在 QT5 本身而在 ExcelFormat 这个第三方库内部用wcstombs和mbstowcs做宽窄字符转换时对多字节路径的长度估算不够中文、日文、韩文路径全被截断。这份QT5-forlinux-basicexcelformat.zip就是针对这个问题的修改版源码核心改动是在BasicExcel.cpp的create和open两个函数里按是否定义NON_ENGLISH_PATH_FILENAME_SUPPORT宏来调整文件名长度计算。它适合正在国产 Linux 系统上做办公类工具、需要处理 xls 且绕不开中文路径的 QT5 开发者尤其是 aarch64、mips64 这类非 x86 架构的运维和交付同学。2. ExcelFormat 库在 QT5 里的定位为什么不用 QAxObject 和 libxlsxwriter2.1 三种 xls 读写方案的取舍在 Linux 下让 QT5 读写 Excel常见路线有三条。第一条是QAxObject走 COM 组件本质是调用本机安装的 OfficeLinux 上根本没有直接排除。第二条是libxlsxwriter它只写不读而且写的是.xlsx不是.xls遇到老系统导出的二进制 xls 就无能为力。第三条就是 ExcelFormat它基于 BasicExcel 封装纯 C 实现不依赖 Office能同时读写.xls编译出来就是一个静态库或几个 cpp 文件塞进 QT 工程就能用。这份资源选的就是第三条路原因很实际交付环境是统信 UOS客户手里的历史数据全是.xls而且机器架构是 aarch64 和 mips64没有现成的二进制包必须源码编译。ExcelFormat 的接口设计偏底层BasicExcel负责工作簿和工作表的打开、创建、单元格读写ExcelFormat在其上加了格式设置比如字体、对齐、数字格式。它不支持.xlsx也不支持公式计算但对「把数据落成表格、再读回来」这个需求来说够用。资源包里能看到BasicExcel.cpp、BasicExcel.h、ExcelFormat.cpp、ExcelFormat.h四个核心文件加上mainwindow.cpp、main.cpp、mainwindow.ui组成的 demo以及excelformat.pro工程文件。编译产物里还有moc_mainwindow.cpp、moc_mainwindow.o、ui_mainwindow.h这些 QT 自动生成的东西说明它是在 qmake 体系下构建的不是 CMake。2.2 中文路径问题的根因wcstombs 的长度陷阱BasicExcel::open和BasicExcel::create接收的是const wchar_t*宽字符路径。在 Linux 上文件系统调用最终要的是char*窄字符所以库内部用wcstombs把宽字符串转成多字节。问题出在长度计算上原版代码用wcslen(filename)得到宽字符个数然后按这个个数去分配窄字符缓冲区。对纯英文路径一个宽字符转成一个字节没问题但中文在 UTF-8 下通常占 3 个字节wcslen返回的是字符数不是字节数缓冲区直接不够wcstombs写越界或者转换失败open自然打不开。修改版的做法是在BasicExcel.cpp里加条件编译。定义NON_ENGLISH_PATH_FILENAME_SUPPORT时filenameLength取wcslen(filename) * 2给多字节留出余量不定义时保持原逻辑。同时在头文件里加上#define NON_ENGLISH_PATH_FILENAME_SUPPORT。这个* 2不是精确的字节数而是一个保守估计对中日韩路径足够覆盖。更严谨的做法是用wcstombs(nullptr, filename, 0)先探测所需长度但修改版选择了改动最小的方案避免影响原有英文路径的行为。2.3 把库挂进 QT5 工程的 qmake 配置资源里的excelformat.pro是理解整个工程怎么组织的关键。它把BasicExcel.cpp、ExcelFormat.cpp、mainwindow.cpp、main.cpp一起编进来没有单独编成库再链接。这样做的好处是 demo 自包含拿到就能qmake make。下面是一个精简后的.pro写法和资源里的结构一致QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET excelformat TEMPLATE app # 核心源文件BasicExcel 和 ExcelFormat 直接参与编译 SOURCES main.cpp \ mainwindow.cpp \ BasicExcel.cpp \ ExcelFormat.cpp HEADERS mainwindow.h \ BasicExcel.h \ ExcelFormat.h FORMS mainwindow.ui # 关键打开非英文路径支持宏 DEFINES NON_ENGLISH_PATH_FILENAME_SUPPORT这里DEFINES NON_ENGLISH_PATH_FILENAME_SUPPORT和头文件里的#define二选一即可资源里是在.h中定义.pro里没重复。SOURCES里四个 cpp 的顺序不影响编译但BasicExcel.cpp必须在ExcelFormat.cpp之前被链接器看到因为后者依赖前者的符号。如果编译时报undefined reference to BasicExcel::...先检查这两个文件有没有都加进SOURCES。3. 从零编译到读写跑通aarch64 与 mips64 上的完整操作链3.1 环境准备与 qmake 构建统信 UOS 专业版默认带的是 gcc 和 qt5 的开发包但 aarch64 和 mips64 上不一定预装qtbase5-dev。先确认工具链# 确认 gcc 和 qmake 版本 gcc --version qmake --version # 如果 qmake 缺失安装 qt5 开发包UOS 基于 debian 系 sudo apt install qtbase5-dev qt5-qmake qtchooserqmake --version输出里要能看到Using Qt version 5.x.x版本号不用纠结QT5 的 5.9 到 5.15 都能编。资源正文提到测试通过统信 UOS、aarch64、mips64、gcc 编译说明它没有用任何架构相关的汇编或 intrinsics纯 C 跨架构。解压资源包后进入目录执行# 生成 Makefile qmake excelformat.pro # 并行编译-j 后面跟 CPU 核心数 make -j4编译产物里会出现moc_mainwindow.o、mainwindow.o、BasicExcel.o、ExcelFormat.o、main.o最后链接成可执行文件。如果make报cannot find -lQt5Widgets说明qtbase5-dev没装全补装libqt5widgets5和对应的 dev 包。mips64 上编译时间会比 aarch64 长不少-j4可能内存吃紧改成-j2更稳。3.2 中文路径读写的代码调用方式demo 的mainwindow.cpp里有使用例子核心是构造BasicExcel对象用wchar_t路径打开或创建再操作工作表。下面这段代码展示了写入和读取的完整流程路径里故意带了中文#include BasicExcel.h #include QDebug void demoWriteRead() { // 中文路径宽字符字面量用 L 前缀 const wchar_t* filePath L/home/user/测试数据/报表.xls; // 写入创建新工作簿 BasicExcel workbook; if (!workbook.New(1)) { // 参数 1 表示创建 1 个工作表 qDebug() 创建工作簿失败; return; } BasicExcelWorksheet* sheet workbook.GetWorksheet(0); if (!sheet) { qDebug() 获取工作表失败; return; } // 写入中文内容SetCell 接收宽字符串 sheet-Cell(0, 0)-SetWString(L姓名); sheet-Cell(0, 1)-SetWString(L部门); sheet-Cell(1, 0)-SetWString(L张三); sheet-Cell(1, 1)-SetWString(L运维); // 保存路径含中文 if (!workbook.SaveAs(filePath)) { qDebug() 保存失败检查路径权限和宏定义; return; } // 读取重新打开 BasicExcel readBook; if (!readBook.Load(filePath)) { qDebug() 打开失败中文路径支持未生效; return; } BasicExcelWorksheet* readSheet readBook.GetWorksheet(0); QString name QString::fromWCharArray(readSheet-Cell(1, 0)-GetWString()); qDebug() 读回姓名: name; }New(1)的参数是工作表数量传 1 就是单表。Cell(row, col)的行列都从 0 开始SetWString写宽字符串GetWString读出来是const wchar_t*用QString::fromWCharArray转成 QT 字符串。SaveAs和Load接收const wchar_t*中文路径必须用L前缀的宽字面量或者从QString转QString path ...; std::wstring wpath path.toStdWString();再传wpath.c_str()。如果这里传的是char*编译都过不了。3.3 表格样式设置与格式参数ExcelFormat 的格式能力通过ExcelFormat类暴露可以设置字体、对齐、数字格式。下面给表头加粗并居中#include ExcelFormat.h // 在写入单元格后设置格式 ExcelFormat* headerFormat workbook.GetFormat(); // 获取默认格式对象 headerFormat-SetFontBold(true); headerFormat-SetHorzAlign(ExcelFormat::ALIGN_CENTER); headerFormat-SetVertAlign(ExcelFormat::ALIGN_CENTER); // 把格式应用到表头单元格 sheet-Cell(0, 0)-SetFormat(headerFormat); sheet-Cell(0, 1)-SetFormat(headerFormat);SetFontBold(true)加粗SetHorzAlign和SetVertAlign控制水平和垂直对齐取值是ExcelFormat里的枚举。数字格式用SetNumberFormat比如headerFormat-SetNumberFormat(0.00)保留两位小数。注意格式对象是工作簿级别的多个单元格可以共用同一个ExcelFormat*不要每个单元格都new一个否则文件体积会涨。资源正文提到「可设置表格样式」指的就是这套接口但 demo 里只做了最基础的演示复杂样式需要自己翻ExcelFormat.h里的方法声明。4. 避坑与排查中文路径、架构差异和编译报错4.1 现象英文路径正常中文路径 open 返回 false原因BasicExcel.cpp里wcstombs的目标缓冲区按wcslen分配中文 UTF-8 字节数大于字符数缓冲区溢出或转换中断。解决确认BasicExcel.h里有#define NON_ENGLISH_PATH_FILENAME_SUPPORT或者.pro里有DEFINES NON_ENGLISH_PATH_FILENAME_SUPPORT。改完必须qmake重新生成 Makefile 再make只改 cpp 不重新 qmake 有时不会触发重编。验证方法在open函数入口加一行qDebug() filenameLength: filenameLength;看中文路径下这个值是不是英文路径的两倍左右。4.2 现象mips64 上编译通过但运行时段错误原因mips64 的对齐要求比 aarch64 严格BasicExcel内部有些结构体指针转换没做对齐处理或者wchar_t在 mips64 上是 4 字节而代码里按 2 字节假设。解决先确认工具链的wchar_t大小echo | gcc -E -dM - | grep WCHAR看__WCHAR_MAX__。如果是 4 字节检查所有sizeof(wchar_t)相关计算。资源正文说测试通过 mips64说明修改版已经处理过但如果自己改代码引入新的指针转换要重新验证。运行时段错误可以用gdb ./excelformat然后bt看栈定位到具体行。4.3 现象make 报 moc_mainwindow.cpp 找不到 ui_mainwindow.h原因QT 的 uic 工具没有把.ui转成ui_mainwindow.h通常是FORMS mainwindow.ui没写或者 qmake 没跑。解决检查.pro里FORMS行然后qmake重新生成 Makefile。如果ui_mainwindow.h已经存在但报错可能是路径问题make clean后重新qmake make。资源包里已经带了ui_mainwindow.h和moc_mainwindow.cpp说明作者是在编译后打包的自己重新编译时这些文件会被覆盖不用手动改。4.4 现象读取 xls 时中文变成乱码原因写入时用了SetString而不是SetWStringSetString接收char*在 UTF-8 环境下中文会被截断。解决所有涉及中文的单元格写入统一用SetWString读取用GetWString再转QString。如果是从旧文件读出来乱码先用Load打开检查GetWString返回的宽字符串是否完整可以在qDebug里用QString::fromWCharArray打印。另外注意 xls 内部存储是 UTF-16ExcelFormat 负责转换只要写入端用宽字符读出来就不会乱。4.5 现象统信 UOS 上文件保存成功但用 WPS 打开提示格式错误原因SaveAs时工作簿没有正确设置 BIFF 版本或者写入过程中BasicExcel的流被提前关闭。解决确保SaveAs之前所有SetWString和SetFormat都已调用完毕SaveAs之后不要再操作该BasicExcel对象。如果问题依旧用 demo 里最简单的写入逻辑测试排除是格式设置导致的。资源正文说「完美支持中文」指的是路径和内容不包含所有 Excel 高级特性复杂公式和图表不在支持范围内。5. 进阶技巧用 wcstombs 探测长度替代硬编码乘 2修改版用wcslen(filename) * 2解决中文路径这个方案对中日韩够用但遇到 emoji 或某些四字节 UTF-8 字符* 2可能还是不够。更稳的写法是先探测所需字节数再分配缓冲区。下面这段代码可以直接替换BasicExcel.cpp里create和open中的长度计算逻辑#ifdef NON_ENGLISH_PATH_FILENAME_SUPPORT // 先探测转换后需要的字节数不写入 size_t filenameLength wcstombs(nullptr, filename, 0); if (filenameLength (size_t)-1) { // 转换失败可能是 locale 没设置 return false; } filenameLength 1; // 留一个字节给结尾的 \0 #else size_t filenameLength wcslen(filename); #endif char* narrowPath new char[filenameLength]; size_t converted wcstombs(narrowPath, filename, filenameLength); if (converted (size_t)-1) { delete[] narrowPath; return false; } // 后续用 narrowPath 调用 fopen 等wcstombs(nullptr, filename, 0)是标准做法返回转换到多字节所需的字节数不实际写入。拿到长度后加 1 给\0再分配和转换。这样无论路径里是中文、日文还是 emoji都不会截断。注意wcstombs依赖当前 locale如果程序启动时没调setlocale(LC_ALL, )在某些 UOS 版本上可能返回 -1。在main.cpp开头加一行setlocale(LC_ALL, );能避免这个玄学问题。我自己的习惯是凡是涉及宽窄字符转换的库编译前先跑一个最小 demo 验证 locale 和转换函数确认没问题再往工程里集成。从那以后每次在国产 Linux 上碰 xls 读写都强制走一遍「英文路径 → 中文路径 → 中文内容 → 读回校验」四步省得交付现场翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表