【QT】使用Qxlsx读取Excel单元格中函数表达式的结果值

零、起因

是这样的,目前朋友托我写一款模板生成软件,任务是先把他写的程序文件复制一份出来,然后再根据Excel中对应位置的单元格的值,修改程序文件副本中的某些文件。对于读Excel的需求,经过测试,最终选择Qxlsx这款开源QT组件来读取Excel中的值,但是Excel中有些单元格用的是Excel函数,使用Document::read函数读出来的是Excel函数表达式,而不是Excel函数表达式函数的结果。故开启本文……

壹、调查

网络调查

通过询问AI、搜索等方式,尝试寻求使用Qxlsx读取Excel单元格中函数表达式结果值的方法,可惜的是我没有找到相应的方法。



不过在与AI对话的过程中,我得到了一个重要的线索:



在AI列举的这些库中,大多都是不支持Excel函数表达式值的读取的,想一想也正常,毕竟写一套解析引擎代码比写一套读取内容代码复杂多了,后面是想参考一下其他能读Excel函数表达式值的库的做法,问下来发现openpyxl支持读Excel函数表达式值,而且原理这里也提到了,是因为Excel文件中的函数表达式的结果值会被保存在单元格中,而openpyxl并不是经过计算得出的,而是直接读出保存在单元格中的结果值的。而且从最后一句话中,我们应该能得出这样的结论:Excel文件保存了什么就只能读出什么,没有保存的是读不出的。

既然得到这样的线索,而且我们也能拿到Qxlsx的源码,那在读取阶段只需要读一下结果值存在Cell对象中不就可以了?

XLSX文件结构调查

众所周知,.xlsx文件本质上是一个压缩包,于是我先创建了一个包含有函数表达式的Excel文件并保存为excel.xlsx



excel.xlsx文件使用7z解压,得到如下目录结构:

Microsoft Windows [版本 10.0.22631.4460]
(c) Microsoft Corporation。保留所有权利。 D:\y17mm\Desktop\xlsx文件分析>tree /f
卷 井中月 的文件夹 PATH 列表
卷序列号为 FE8F-1AB3
D:.
│ excel.xlsx

└─excel
│ [Content_Types].xml

├─docProps
│ app.xml
│ core.xml
│ custom.xml

├─xl
│ │ sharedStrings.xml
│ │ styles.xml
│ │ workbook.xml
│ │
│ ├─theme
│ │ theme1.xml
│ │
│ ├─worksheets
│ │ sheet1.xml
│ │
│ └─_rels
│ workbook.xml.rels

└─_rels
.rels D:\y17mm\Desktop\xlsx文件分析>

简单观察文件后发现,表格文件里Sheet1的数据就存在excel/xl/worksheets/sheet1.xml中,汉字不存具体的值(但这个不需要我们关心,是从excel/xl/sharedStrings.xml中映射过来的),使用标签v与其他xml文档做映射,而函数表达式会存下来,标签是f,省略了等于号,函数表达式的值也会存下来,就与公式在同一个c标签中,用与保存其他文字相同的标签v来保存……

excel/xl/worksheets/sheet1.xml的内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet
xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:etc="http://www.wps.cn/officeDocument/2017/etCustomData">
<sheetPr/>
<dimension ref="A1:B2"/>
<sheetViews>
<sheetView tabSelected="1" workbookViewId="0">
<selection activeCell="B2" sqref="B2"/>
</sheetView>
</sheetViews>
<sheetFormatPr defaultColWidth="8.88888888888889" defaultRowHeight="14.4" outlineLevelRow="1" outlineLevelCol="1"/>
<cols>
<col min="1" max="1" width="9.11111111111111" customWidth="1"/>
</cols>
<sheetData>
<row r="1" spans="1:2">
<c r="A1" t="s">
<v>0</v>
</c>
<c r="B1" t="s">
<v>1</v>
</c>
</row>
<row r="2" spans="1:2">
<c r="A2" s="1" t="s">
<v>2</v>
</c>
<c r="B2" t="str">
<f>LEFT(B1,1)</f>
<v>钟</v>
</c>
</row>
</sheetData>
<pageMargins left="0.75" right="0.75" top="1" bottom="1" header="0.5" footer="0.5"/>
<headerFooter/>
</worksheet>

excel/xl/sharedStrings.xml的内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst
xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="3" uniqueCount="3">
<si>
<t>名字</t>
</si>
<si>
<t>钟离</t>
</si>
<si>
<t>姓氏</t>
</si>
</sst>

真的有保存函数表达式的结果值,那Qxlsx实现读取Excel单元格中函数表达式的结果值就具有了可行性!

源码追踪调查

按理说Qxlsx的Document::read函数读出来的应该就是c标签中v标签的内容呀,可为啥读到的是c标签中f标签呢?



通过跟踪Qxlsx的读取单元格值的源码,不难发现这样一个函数:



发现在读取值时会先判断是否有公式,如果有,则返回等于号加公式文本,否则不是公式也不是日期时间,则使用cell->value()返回cell中的值。到这里,应该能想到解决方案了,我们只需要调用这个cellAt函数,并且不需要管是否有函数公式,直接调用返回value()的结果就好了。

贰、解决

基于以上结论,不难写出这样一个函数用于读取Excel中单元格函数表达式的结果值:

#include "xlsxdocument.h"
#include "xlsxcellformula.h"
#include "xlsxcell.h"
#include <QDebug> bool isDebug = true; QVariant readCellValue(QXlsx::Document& xlsx, const int& row, const int& col, const bool readVal=true){
if(readVal) {
auto* ws = xlsx.currentWorksheet();
auto *cell = ws->cellAt(row, col);
if(cell != nullptr && cell->hasFormula()) { // 存在公式
if(isDebug) qDebug()<<"公式";
if(isDebug) qDebug()<<"cell->formula().isValid()"<<cell->formula().isValid();
if(isDebug) qDebug()<<"cell->formula().formulaText()"<<cell->formula().formulaText();
if(isDebug) qDebug()<<"cell->formula().formulaType()"<<cell->formula().formulaType();
if(isDebug) qDebug()<<"cell->value()"<<cell->value();
QVariant v = cell->value();
QVariant emptyVariant;
if(v.toString().compare(emptyVariant.toString()) == 0) { // 如果值为空
return "=" + cell->formula().formulaText(); // 返回公式
} else {
return cell->value(); // 返回计算值
}
}
} // cell->d_func()->value
return xlsx.read(row, col);
}

要读取值时判断是否存在函数表达式,如果存在,则尝试获取函数表达式的值,如果函数表达式有有效值则返回值,否则按照默认方式返回函数表达式。

经过调整,我们成功地拿到了Excel单元格中函数表达式的结果值:

叁、总结

Excel表格单元格中函数表达式的结果值一般会存在表格文件中的,理论上只要能读取xlsx表格的单元格的值的代码经过稍加改造都可以读到Excel单元格中函数表达式的结果值。本次Qxlsx读取Excel单元格中函数表达式的结果值代码总结如下:

    int row = 2;  // 行
int col = 2; // 列
QString xlsxFile = "D:\\y17mm\\Desktop\\xlsx文件分析\\excel.xlsx"; // 文件路径
QXlsx::Document xlsx(xlsxFile);
auto* ws = xlsx.currentWorksheet();
auto *cell = ws->cellAt(row, col);
auto ret = cell->value();
qDebug()<<"读取结果:"<<ret;

肆、参考资料

  1. Qxslx https://github.com/dbzhang800/QtXlsxWriter
  2. AI对话内容 https://yiyan.baidu.com/share/0zdNk8Z7VN?utm_invite_code=A66Y52pyQAd0FFd681/Jqw==&utm_name=5bCP6bG85pWP5ZOy&utm_fission_type=common

【QT】使用Qxlsx读取Excel单元格中函数表达式的结果值的更多相关文章

  1. C# 开源组件--NPOI读取Excel单元格中的公式值

    今天在项目中碰到了EXCEL导入的数据是用公式生成,直接导入不了数据,写在博客中方便自已查询也可以给想找这方面的参考一下: 用NPOI导入时,在OFFICE 2007中的文件导入时一般会用XSSF,所 ...

  2. 如何在excel单元格中插入图片批注

    在excel单元格中插入图片批注的方法: 1.选定要插入图片的单元格,然后右键选择插入批注. 2.然后会插入一个批注框,为了不影响图片效果,可以将批注文字都删除.然后鼠标移动到批注框边角再右键. 3. ...

  3. poi 升级至4.x 的问题总结(POI Excel 单元格内容类型判断并取值)

    POI Excel 单元格内容类型判断并取值 以前用 cell.getCachedFormulaResultType() 得到 type 升级到4后获取不到了 换为:cell.getCellType( ...

  4. C# ASP.NET 读取EXCEL 单元格 读取 空值 不显示

    跟大家分享一下,[摘自]:http://blog.csdn.net/li185416672/article/details/8213729 读取excel时,某些单元格为空值 原来如此: 当我们用ol ...

  5. excel单元格中批量加入固定字符

    excel单元格前怎么批量加字母 现在我要在联系人这列,每个姓名前加入衡阳的首字母简写(HY). 3 在同行上面随便找列,我找D列.输入公式:="HY"&A2. 5 输入后 ...

  6. 通过VBA,当在EXCEL单元格中输入任意的日期格式时,都能自动转换为指定的标准格式的日期值

    在日常录入EXCEL表格的单元格里 ,我们输入一些一般性的日期内容,如:2017-10-17 或 2017/10/17时,EXCEL会自动识别为日期并按单元格设计格式显示,单元格中存储的值也是日期格式 ...

  7. POI如何自动调整Excel单元格中字体的大小

    问题 目的是要将Excel中的文字全部显示出来,可以设置对齐格式为[缩小字体填充],但是这样的话只能展示出一行数据,字体会变得很小.还有一种办法,设置对齐格式为[自动换行],然后让单元格中的字体自动调 ...

  8. Java 在Excel单元格中应用一种/多种字体样式

    在Excel表格中,设置单元格字体样式时,可以对单元格内的所有字符应用同一样式,即获取指定单元,应用样式即可:另外也可以对单元格内的不同字符内容应用不同字体样式,即获取单元格中的字符位置,应用样式:本 ...

  9. C#/VB.NET 在Excel单元格中应用多种字体格式

    在Excel中,可对单元格中的字符串设置多种不同样式,通常只需要获取到单元格直接设置样式即可,该方法设置的样式会应用于该单元格中的所有字符.如果需要对单元格中某些字符设置样式,则可以参考本文中的方法. ...

  10. 在excel单元格中提取信息

    平时在excel中处理数据的时候,肯定会遇到在单元格提取信息的情况,比如在地址中提取省.市.地区等,如果数据源内容规整的话,可以直接使用left().right().mid()等函数直接提取,但是大多 ...

随机推荐

  1. CDS标准视图:维修工单工艺数据 I_MAINTORDEROPERATIONDATA

    视图名称:维修工单工艺数据 I_MAINTORDEROPERATIONDATA 视图类型:基础 视图代码: 点击查看代码 @EndUserText.label: 'Maintenance Order ...

  2. 前端(一)-Html

    1.网页基本信息 <!DOCTYPE html> 浏览器使用的规范 <head> 网页头 <body> 主体部分 <meta> 元数据 meta的nam ...

  3. Linux 开发环境常用配置

    记录下我个人 Linux 开发环境常用配置,后续可以简单写个小脚本可以把一系列安装配置操作自动化一把. zsh install & conf https://blog.csdn.net/amo ...

  4. Codeforces Round 962 (Div. 3)

    题目链接:Codeforces Round 962 (Div. 3) 总结:ABC秒过,D有点难评了,E优化很妙. A. Legs tag:签到 void solve(){ cin >> ...

  5. uni-app配置顶部标题样式

    在pages.json中,通过配置这个文件,可以去设置当前页面的标题样式, 赋值的时候,将注册删除哈!!! 这样配置兼容 小程序和H5端 在配置的时候,没有给背景色,我还以为在uniapp中不兼容小程 ...

  6. 最大流的 Dinic 算法和 ISAP 算法

    上期回顾:https://www.cnblogs.com/ofnoname/p/18678895 之前我们已经介绍了最大流问题的基本定义.最大流最小割定理.增广路径与残量网络的构建方法,以及如何利用这 ...

  7. Palworld幻兽帕鲁世界参数修改最佳实践(Ubuntu)

    本文为您介绍对Palworld游戏世界参数进行修改的最佳实践. 操作场景 本文以Ubuntu操作系统为例,为您介绍通过Palworld专有镜像一键部署Palworld服务器后对游戏世界参数进行修改的具 ...

  8. VScode中C/C++调试文件配置

    VScode中C/C++调试文件配置 //launch.json { "version": "2.0.0", "configurations" ...

  9. windows的恶意代码自定义

    代码执行步骤: 1,新建"文本文档" 2,输入代码 3,@echo off format C: /q/u/y 4,保存"文本文档" 文本文档.txt改为.bat ...

  10. Typecho博客后台登陆界面美化

    登录界面: 食用方法: 备份 admin 目录 压缩包内容上传到 admin 目录内. 结构:网站根目录 /admin/login.php 结构:网站根目录 /admin/style 修改 login ...