“
阅读本文大概需要 6 分钟
日常开发软件可能会遇到这类小众需求,导出数据到 Word、Excel 以及 PDF文件,如果你使用 C 编程语言,那么可以选择的方案不是很多,恰好最近刚好有这部分需求,整理下这段时间踩过的坑,方便后人
读写 Word日常开发的软件使用最多的应该是导出数据到 Word 文档中,目前可以用的方案有这几种
没有十全十美的方案,任何方案都存在优点和缺点,下面来详细看下这几种方案的优缺点以及适用场景
XML 模板替换优点“
原理:事先编辑好一份 Word 模板,需要替换内容的 地方预留好位置,然后使用特殊字段进行标记,后面使用代码进行全量替换即可完成
- 代码量相对较少、导出速度快
- 跨平台,支持多个系统,系统不安装 office 也能导出;
- 支持图片以及固定格式导出;
- 导出格式固定,可扩展性不强,如果需求变化导出格式变了,那么模板也要跟着改变;
- 一种格式对应一份模板,如果导出格式较多,需要准备的模板文件较多,这样比较繁琐;
- 需要 Word 2003 以上版本;
我们先编辑一份 Word 模板文档,内容大概如下所示:
- 将该文档另存为 Word XML 文档 XML-Template.xml
- 读取文档内容进行变量替换
QFile file("XML-Template.xml");
if (!File.open(QIODevice::ReadOnly))
{
qDebug() << "open xxml file fail. " << file.errorString();
return 0;
}
QByteArray baContent = file.readAll();
file.close();
QString strAllContent = QString::fromLocal8Bit(baContent);
strAllContent.replace("$VALUE0", "1");
strAllContent.replace("$VALUE1", QString::fromLocal8Bit("法外狂徒张三"));
strAllContent.replace("$VALUE2", QString::fromLocal8Bit("考试不合格"));
strAllContent.replace("$VALUE3", "2");
strAllContent.replace("$VALUE4", QString::fromLocal8Bit("李四"));
strAllContent.replace("$VALUE5", QString::fromLocal8Bit("合格"));
QFile newFile("export.doc");
if (!newFile.open(QIODevice::WriteOnly))
{
qDebug() << "file open fail." << newFile.errorString();;
return 0;
}
newFile.write(strAllContent.toLocal8Bit());
newFile.close();
- 保存替换后的内容,写入文件
可以看出来这种方式比较繁琐,重点是编辑固定的模板格式,而且编辑好后保存成XML格式后还需要继续调整,这种 XML 格式标签很多,不小心就修改错了,导致导出的文档打不开
这种方式适合模板内容不太复杂,内容较少的情况下使用
COM 组件方式“
原理:采用 Micro Soft公开的接口进行通讯,进行读写时会打开一个 `Word进程来交互
COM 技术概述
qt 为我们提供了专门进行交互的类和接口,使用 Qt ActiveX框架就可以很好的完成交互工作
优点- 实现简单,快速上手;
- 导出写入速度慢,因为相当于打开 word 文档操作;
- 仅 Windows平台可用,其它平台失效;
- 需要程序运行的电脑安装 office word,否则调用失败
使用时需要引入对应的模块,在 pro 文件引入模块
QT *= axcontainer
打开文档写入内容
QAxObject *pWordWidget = new(std::nothrow) QAxObject;
bool bResult = pWordWidget->setControl("word.Application");
if (!bResult)
{
return false;
}
// 设置是否显示
pWordWidget->setProperty("Visible", false);
QAxObject *pAllDocuments = pWordWidget->querySubObject("Documents");
if(nullptr == pAllDocuments)
{
return false;
}
// 新建一个空白文档
pAllDocuments->dynamicCall("Add (void)");
// 获取激活的文档并使用
QAxObject *pActiveDocument = pAllDocuments->querySubObject("ActiveDocument");
if(nullptr == pActiveDocument)
{
return false;
}
// 插入字符串
QAxObject *pSelectObj = pWordWidget->querySubObject("Selection");
if (nullptr != pSelectObj)
{
pSelectObj->dynamicCall("TypeText(const QString&)", "公众号:devstone");
}
……
可以看出来使用起来不难,对于新手友好一点,很多写入操作方法比较繁琐,需要自己重新封装一套接口
- 这种方案比较适合那些排版比较复杂,图片、文字、表格混排的场景下,而且内容都是动态变化的,可以很好的实现定制化
- 当然了它的缺点也不少,也有一些坑,有时候莫名其妙会失败,还有就是比如你电脑安装的 Word 没有激活,那么每次启动会弹激活窗口
- 还有就是这种方式要求所有的路径必须是本地化的,比如 D:\\Soft\test.png
- 使用前最好读取注册表判断当前电脑是否安装了 Office Word,如果没有安装,直接读取操作肯定会崩溃
这种方式同样适用于写入 Excel 文件,后面再说
HTML 方式优点“
原理:这种方式得益于 Word支持 HTML格式导出渲染显示,那么反向也可以支持,需要我们拼接 HTML格式内容,然后写入文件保存成 .doc格式
- 跨平台,不仅限于 Windows平台,代码可扩展性比较好
- 导出速度快、代码可扩展;
- 字符串拼接 HTML 容易出错,缺失标签导出后无法显示;
- 插入的图片是本地图片文件的链接,导出的 word文档拷贝到其它电脑图片无法显示
QString HTML2Word::getHtmlContent()
{
QString strHtml = "";
strHtml = "<html>";
strHtml = "<head>";
strHtml = "<title>测试生成word文档</title>";
strHtml = "<head>";
strHtml = "<body style=\"bgcolor:yellow\">";
strHtml = "<h1 style=\"background-color:red\">测试qt实现生成word文档</h1>";
strHtml = "<hr>";
strHtml = "<p>这里是插入图片<img src=\"D:\\title.jpg" alt=\"picture\" width=\"100\" height=\"100\"></p>";
strHtml = "</hr>";
strHtml = "</body>";
strHtml = "</html>";
return strHtml;
}
// 保存写入文件
QFile file("D:/htmp2Word.doc");
if (!file.open(QIODevice::WriteOnly))
{
return false;
}
QTextStream out(&file);
out << getHtmlContent();
file.close();