当前位置: 首页 > news >正文

C/C++ 从 Excel (.xls)文件中提取图像 - capp

C/C++ 从 Excel (.xls)文件中提取图像

xls文件相关背景以及大致结构(以下来源于网络

Microsoft Excel 97-2003 工作簿(.xls)文件采用了一种基于 OLE2(复合文档文件格式,也称 Compound File Binary Format)的二进制文件结构。这种格式与后来的基于 XML 的 .xlsx 格式(自 Office 2007 起)有着本质的不同。

  1. OLE2 复合文件结构: .xls 文件本质上是一个“文件系统”,内部包含多个Streams和Storages。关键数据,如工作表内容、图表、宏以及嵌入的对象(包括图片),都存储在这些不同的流中。其中,WorkbookBook 流是核心,包含了主要的工作表数据和元信息。

  2. BIFF (Binary Interchange File Format) 格式:Workbook 流内部,数据是以一系列被称为“记录”(Records)的二进制块形式组织的。这些记录遵循 BIFF (Binary Interchange File Format) 规范。每个记录都有一个记录类型(Record Type)、记录大小(Record Size)和记录数据(Record Data)。不同的记录类型代表了不同的含义,例如 BOF (Beginning of File) 标记流的开始,Row 记录表示行信息,Formula 记录表示单元格公式等。

嵌入图像的存储

  1. 将png或者jpeg文件插入到excel中时,这些图片并非直接嵌入在某个具体的单元格中,而是被编码并存储到某些BIFF记录中,这些记录主要是MSoDrawingGroup类型的记录。

  2. 直接从 .xls 文件中提取图片,意味着需要深入理解OLE2容器的解析方法,以及 BIFF 记录的复杂结构。特别是MsoDrawingGroup记录中的图片数据可能被分割到多个连续的Continue记录中,增加了提取的难度。

本人对于从xls中提取图像的看法

  1. 我并非是一开始就想实现从xls中提取图像,而是在开发一款图像搜索软件时,客户需要匹配excel中的相似图片,对于新版格式(.xlsx)那可是相当好提取,手搓一个解析器都没什么困难,可是对于本文这种旧版格式(.xls)还是花费了大量的时间精力的。

  2. 导致这么格式难解析的主要原因并非是这种格式不公开,这种格式是微软自家的,写的文档可谓是十分详细,单说其中一个文件 MS-XLS就有1000+页,你可以说微软那不好,但必须要承认微软文档这块做的没话说,还是非常好的,但是对于这个可以算是遗留版本excel格式,难解析也是必然的,这也是万恶的历史遗留问题。

  3. 也不多说了,说说真正难解析的,这其实是网上很少(几乎没有)有较为完整代码来从头到尾解析的,大多都是简单讲几句就略过了,这还是我之前说的那个原因,xls文件极其复杂,这就导致没有多少人愿意去实实在在的解析它,只有JAVA的一个office库有完整的接口,除外就是商业解决方案,而注意我现在像用c或者cxx这种底层语言调用第三方库想去解析一个xls并获取其中的图像,据我所知,全网都没有这样的专门用于解析xls中的图像的开源c库。

  4. 基于以上分析,我打算手搓一个能够解析xls中的workbook流并提取出其中的图像(像是png或者是jpeg格式),为此我查阅了大量资料,但还是让我绷不住的是微软的文档,太长了,在当我提取完workbook流并获取每个图像的MsoDrawingGroup数据后我放弃了官方文档,转而设计并采用一种专门针对此任务的启发式算法,虽然这种算法从理论上来说不靠谱,但通过我的优化与测试后,无论是性能还是准确度都有优异的成绩。

  5. 顺便说一下,这是我个人开发的第一个c库,应该还存在很多bug,还请多多指教。注意以下提取思路是我项目中的简单介绍,具体还需阅读源码,这是我的存储库https://github.com/capp-adocia/xls2img,感兴趣的可以来看看,如果你喜欢我的xls2img项目请star下,感谢您关注。

  6. 如果有些朋友不想看这些分析,只想要个能够解析的工具的,可以直接去我存储库的Release中,里面有一个zip文件下载下来,用命令行的方式就能对指定的xls文件提取其中图片了。

提取思路

  1. 获取 WorkBook 流

    • 根据微软相关文档,像 JPG、PNG 这样的文件都存储在 WorkBook 流中。因此,首先需要解析 XLS 的文件格式。了解 XLS 格式非常困难,感谢微软的 compoundfilereader 仓库,它极大地简化了这一流程,使我们能够从 XLS 中解析出 WorkBook 流。
  2. 从 WorkBook 流中查找 MsoDrawingGroup

    • WorkBook 流由多个 BIFF8 结构组成,每个 BIFF8 都包含记录类型、记录大小和记录数据。需要提取的图片数据就对应记录类型为 MsoDrawingGroup 的结构。该结构中存储了嵌入在 XLS 中的图像数据。通过遍历每个 BIFF8 结构来获取所有的 MsoDrawingGroup 数据。
    • 由于单个 BIFF8 的存储有上限,一个图像的数据可能会被分割存储在多个 BIFF8 中。幸运的是,每当图像数据被分割时,除了第一个 BIFF8 外,后续的数据块会以 recordType = BIFF8_CONTINUE 作为标志,这使得我们可以将这些分离的数据块重新组合。
  3. MsoDrawingGroup 中解析图像数据

    • MsoDrawingGroup 中不仅包含图片数据,还包含一些元信息,因此不能直接将其写入文件。需要进一步解析。微软官方文档虽然存在,但篇幅冗长,直接用于解析图片较为复杂。
    • 通过分析 MsoDrawingGroup 的二进制数据,发现其中大部分内容都是图像数据。因此,选择使用 启发式算法 来解析。
  4. 启发式算法解析图像数据

    • xls2img 仅针对 PNG 和 JPEG 图像进行处理,其核心思路如下:
      1. PNG: 遍历 MsoDrawingGroup 数据,查找 PNG 的 8 字节文件头签名(89 50 4E 47 0D 0A 1A 0A),这个签名特征明显,误判概率低。结束位置则严格按照 PNG 格式规范查找 IEND 块。
      2. JPEG: 同样遍历 MsoDrawingGroup 数据,查找 JPEG 的文件头签名。常见的签名有 JFIF (APP0) 和 Exif (APP1),这两种格式的签名特征也比较明显,不易误判。对于结束位置,JPEG 文件以 0xFF 0xD9 结尾,但在某些情况下,该字节序列也可能出现在图像数据内部(例如作为图像内容的一部分)。如果从前往后查找,容易找到错误的结束符,导致将一张完整的图片误认为是多张图片。为避免此问题,本库采用从后往前的方式查找 0xFF 0xD9。这种查找的时机是:当找到下一个有效图像文件头时,便从该新文件头的位置向前回溯,寻找上一个图像的结束符。
    • 至此,这两种格式的图片解析完成。如对性能有疑问,请参考下方测试。

最后

如果有什么需要帮助的,请联系我,我会尽可能地帮助你!
qq email -> 2636427505@qq.com

http://www.jsqmd.com/news/371471/

相关文章:

  • Flink从入门到上天系列第三篇:Flink集群化部署
  • B3929 [GESP202312 五级] 小杨的幸运数
  • Kafka从入门到上天系列第八篇:如果直接在zookeeper当中把controller节点直接删除掉。会发生什么?
  • AppML 案例模型:深度解析与应用前景
  • C# 类型转换详解:隐式、显式转换及常用方法
  • Node.js 安装配置指南
  • 智能教育Agentic AI的伦理框架:提示工程架构师的设计原则与实践
  • 按键消抖方法
  • MySQL 安装配置
  • 手把手教你学Simulink--基于高比例可再生能源渗透的复杂电网建模场景实例:多馈入直流系统中光伏电站与风电场协同运行仿真
  • 从模型到产品:Claude AI原生应用商业化路径
  • 使用 MATLAB/Simulink + Simscape Electrical 构建一个包含风光互补发电系统的模型
  • 数据库系统概论第一章
  • 1169: PIPI倒水
  • 数据库系统概论第二章关系数据库
  • AI原生应用里自然语言处理的核心算法解析
  • 数据库系统概论第三章关系数据库标准语言SQL
  • Eureka在大数据领域的核心作用揭秘
  • 突破查重难关!7大AI降重方案解析
  • 毕业论文AI工具推荐:5个高效选择
  • 击穿膨胀痛点:OpenTeleDB 源码编译与 XStore 引擎极限抗压实录
  • 纠结论文写作?5款AI工具实测排名解析
  • 5个靠谱AI写作网站,解决毕业论文纠结问题
  • 5个高评分AI写作网站,论文效率翻倍
  • 构建之法阅读笔记3
  • 2026年踩了5次坑后,我终于搞懂了降AI率的正确姿势
  • 再讨论一次视频平台接入摄像机要注意的问题
  • C# `async/await` 技术笔记
  • 论文降重指南:7个AI工具实测推荐
  • 降AI率工具怎么用?从上传到出结果手把手教你3步搞定