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

利用DeepSeek辅助WPS电子表格ET格式分析

前面研究了电子表格的xlsx、xlsb、ODS格式,接下来研究WPS电子表格ET格式,这个格式网上的资料很少,WPS官方说 .et 格式是兼容 Xlsx 和 Xls 的。
然而,把它后缀改为xlsx,rust_sheet插件还是无法识别,报错

Binder Error:
Read spreadsheet failed: Invalid xlsx file format: File not found 'xl/_rels/workbook.xml.rels'

而把后缀改为xls,rust_sheet插件就能识别,怎么解释这样神奇的现象?

mv HelloWorld.et HelloWorld.et.xls./duckdb132D load rusty_sheet;
D from read_sheet('HelloWorld.et.xls');
┌─────────────┐
│ Hello World │
│   varchar   │
├─────────────┤
│   0 rows    │
└─────────────┘

这要从xls格式的来源说起,以下是DeepSeek的详细说明

Microsoft Excel 97-2003 (XLS) 文件格式详解

Excel 97-2003 使用的 XLS 格式(也称为 BIFF8 格式)是 Microsoft 的二进制文件格式,在 Excel 2007 之前一直是标准格式。以下是该格式的全面介绍:

一、基本结构

XLS 文件是基于 复合文件二进制格式(Compound File Binary Format,简称 CFBF)的结构化存储文件,也称为 OLE2 或 DOCFILE 格式。

1. 存储结构特点

  • 类似小型文件系统:包含"存储"(storages)和"流"(streams)
  • 扇区分配:数据被分成512字节的扇区
  • FAT表:文件分配表记录扇区链接关系
  • 目录结构:包含文件内各部分的层次关系

二、核心组成部分

1. 主要存储流

\Workbook       // 主工作簿数据
\SummaryInformation  // 文档摘要信息
\DocumentSummaryInformation // 文档统计信息

2. Workbook 流结构

Workbook 流由一系列 BIFF记录(Binary Interchange File Format)组成:

记录类型说明
BOF工作簿/工作表开始标记
EOF结束标记
SST共享字符串表
BoundSheet工作表信息
DIMENSIONS工作表尺寸
ROW行信息
CELL单元格数据
FORMULA公式
FONT字体定义
XF单元格格式
STYLE样式定义

三、关键记录详解

1. 单元格数据存储

  • 数字:直接以8字节双精度浮点存储
  • 字符串:引用SST表中的索引
  • 布尔值:0(FALSE)或1(TRUE)
  • 错误值:特定错误代码
  • 空白单元格:不存储,通过ROW记录推断

2. 共享字符串表(SST)

struct {uint16_t numStrings;      // 字符串总数uint16_t numUniqueStrings;// 唯一字符串数XLUnicodeString strings[];// 字符串数组
}

3. 公式存储

struct {uint16_t row;uint16_t col;uint16_t xfIndex;uint64_t value;  // 缓存的计算结果byte formula[];  // 公式的RPN表示
}

四、格式限制

项目限制
行数65,536 (2^16)
列数256 (2^8)
工作表数受内存限制
单元格样式4,000种左右
字符串长度32,767字符

五、与新版XLSX对比

特性XLSXLSX
格式类型二进制XML压缩包
文件大小相对较大较小
恢复能力
最大行数65,5361,048,576
扩展性有限良好
加密支持

六、编程处理

1. 使用Python读取

import xlrd# 打开XLS文件
workbook = xlrd.open_workbook("example.xls")# 获取第一个工作表
sheet = workbook.sheet_by_index(0)# 读取单元格数据
print(sheet.cell_value(0, 0))  # 第一行第一列

2. 使用Python写入

import xlwt# 创建工作簿
workbook = xlwt.Workbook()
sheet = workbook.add_sheet("Sheet1")# 写入数据
sheet.write(0, 0, "Hello World")# 保存文件
workbook.save("output.xls")

它的结构可以使用专业工具检查:

from olefile import OleFileIOtry:ole = OleFileIO("test.xls")print("有效的OLE结构:", ole.listdir())
except:print("不是完整的OLE文件")

我们用上述xlwt脚本生成一个HelloWorld.xls,用xlrt和olefile分别去读。

python3 xlrd2.pyHello Worldpython3 ole2.py
有效的OLE结构: [['Workbook']]

然后用WPS打开HelloWorld.xls,再另存为HelloWorld.et,用xlrt和olefile分别去读。

python3 xlrd2.pyHello Worldpython3 ole2.py
有效的OLE结构: [['\x05DocumentSummaryInformation'], ['\x05SummaryInformation'], ['ETExtData'], ['Workbook']]

可见,et格式是有效的OLE结构,但比标准xls格式多了3个额外的段,不影响读取。

那么它怎么又能兼容xlsx格式呢?问题就出在多出来的段上。

我们知道xlsx实际是zip格式,那ET也是zip格式吗,不完全是。
我用unzip工具去列出它包含的文件,结果是这样的。

unzip -l HelloWorld.et
存档文件:  HelloWorld.et
warning [HelloWorld.et]:  10223 extra bytes at beginning or within zipfile(attempting to process anyway)Length      Date    Time    Name
---------  ---------- -----   ----540  2012-07-02 09:52   [Content_Types].xml0  2012-07-02 09:52   _rels/310  2012-07-02 09:52   _rels/.rels0  2012-07-02 09:52   theme/0  2012-07-02 09:52   theme/theme/0  2012-07-02 09:52   theme/theme/_rels/283  2012-07-02 09:52   theme/theme/_rels/themeManager.xml.rels6432  2012-07-02 09:52   theme/theme/theme1.xml138  2012-07-02 09:52   theme/theme/themeManager.xml
---------                     -------7703                     9 files

unzip发现这个文件除了zip格式应该有的部分,还有10223个多余的字节,而且还能继续解压,但多余的字节解压不了,也就取不到真正的数据。
需要特别说明,WPS保存的xls格式并非标准xls格式,仅仅是改个后缀。它利用了OLE结构允许多个段,而读取xls格式的程序只去读’Workbook’段这个特点实现了“兼容”。本质上就是带有xls段和zip信息的混合格式。
再将它与xlsx格式比较,用WPS打开HelloWorld.et,再另存为HelloWorld.xlsx,用unzip工具去列出:

unzip -l HelloWorld.xlsx
存档文件:  HelloWorld.xlsxLength      Date    Time    Name
---------  ---------- -----   ----1293  2012-07-02 09:52   [Content_Types].xml0  2012-07-02 09:52   _rels/735  2012-07-02 09:52   _rels/.rels0  2012-07-02 09:52   docProps/564  2012-07-02 09:52   docProps/app.xml573  2012-07-02 09:52   docProps/core.xml526  2012-07-02 09:52   docProps/custom.xml0  2012-07-02 09:52   xl/0  2012-07-02 09:52   xl/_rels/698  2012-07-02 09:52   xl/_rels/workbook.xml.rels187  2012-07-02 09:52   xl/sharedStrings.xml20006  2012-07-02 09:52   xl/styles.xml0  2012-07-02 09:52   xl/theme/6432  2012-07-02 09:52   xl/theme/theme1.xml477  2012-07-02 09:52   xl/workbook.xml0  2012-07-02 09:52   xl/worksheets/1309  2012-07-02 09:52   xl/worksheets/sheet1.xml
---------                     -------32800                     17 files

可见,这个文件没有多余字节,真正的数据保存在sharedStrings.xml和xl/worksheets/sheet1.xml中,这在上文已经分析过了。

为什么WPS能这么干呢?因为zip格式是允许文件中间有多余部分的,以下是DeepSeek的详细解释。

ZIP 文件格式详解及安全修改指南

ZIP 文件格式基本结构

ZIP 文件是一种常见的归档文件格式,其基本结构如下:

1. 核心组成部分

[Local File Header 1]
[File Data 1]
[Local File Header 2]
[File Data 2]
...
[Central Directory]
[End of Central Directory Record]

2. 关键结构说明

  • Local File Header:每个文件的本地头,包含文件名、压缩方法等元数据

    • 签名:0x04034b50 (PK\x03\x04)
    • 结构:30字节固定部分 + 文件名 + 额外字段
  • File Data:实际压缩后的文件内容

  • Central Directory:中央目录,包含所有文件的索引信息

    • 签名:0x02014b50 (PK\x01\x02)
    • 包含所有文件的完整路径、压缩信息、偏移量等
  • End of Central Directory Record:目录结束标记

    • 签名:0x06054b50 (PK\x05\x06)
    • 包含中央目录的位置和文件总数

在ZIP中添加内容而不影响解压的方法

方法1:利用ZIP注释字段(推荐)

import zipfiledef add_to_zip_comment(zip_path, extra_data, output_path):with zipfile.ZipFile(zip_path, 'a') as zf:# 获取原注释orig_comment = zf.comment# 追加新数据new_comment = orig_comment + extra_data.encode('utf-8')# 创建新ZIP文件with zipfile.ZipFile(output_path, 'w') as new_zip:# 复制所有原文件for item in zf.infolist():with zf.open(item.filename) as f:new_zip.writestr(item, f.read())# 设置新注释new_zip.comment = new_comment# 使用示例
add_to_zip_comment('example.zip', '这是我的额外数据', 'modified.zip')

优点

  • 完全符合ZIP规范
  • 所有解压工具都能正常处理
  • 不会影响文件校验和

方法2:在Central Directory后添加数据(兼容性较好)

  1. 找到End of Central Directory Record的起始位置
  2. 在其前面插入额外数据
  3. 更新End of Central Directory Record中的偏移量
def append_after_central_dir(original_zip, extra_data, output_zip):with open(original_zip, 'rb') as f:data = f.read()# 查找End of Central Directory Recordeocd_pos = data.rfind(b'PK\x05\x06')if eocd_pos == -1:raise ValueError("无效的ZIP文件")# 在EOCD前插入数据new_data = data[:eocd_pos] + extra_data.encode() + data[eocd_pos:]with open(output_zip, 'wb') as f:f.write(new_data)

方法3:利用Extra Field字段(针对特定文件)

def add_to_extra_field(zip_path, output_path):with zipfile.ZipFile(zip_path, 'a') as zf:# 获取第一个文件的信息file_list = zf.infolist()if not file_list:raise ValueError("ZIP文件为空")# 修改extra字段file_info = file_list[0]file_info.extra = b'MyExtraData\x00\x01' + file_info.extra# 创建新ZIP文件with zipfile.ZipFile(output_path, 'w') as new_zip:for item in file_list:with zf.open(item.filename) as f:content = f.read()new_zip.writestr(item, content)

注意事项

  1. 签名冲突:确保添加的数据不包含PK\x03\x04、PK\x01\x02等ZIP魔术数字

  2. 文件校验:修改后建议验证ZIP文件完整性

    zipfile.is_zipfile('modified.zip')  # 验证ZIP有效性
    
  3. 大小限制

    • ZIP注释最大65535字节
    • Extra Field字段通常限制为65535字节
  4. 兼容性测试:建议在目标解压环境中测试修改后的文件

高级技巧:隐藏数据

对于需要隐蔽存储的场景,可以:

  1. 将数据加密后存入注释
  2. 使用StegZIP等工具在最低有效位(LSB)隐藏信息
  3. 利用ZIP64扩展存储大容量额外数据

以上方法都能确保标准解压工具正常解压原始内容,同时携带额外信息。

http://www.dtcms.com/a/337124.html

相关文章:

  • 代码随想录算法训练营四十五天|图论part03
  • flask——4:请求与响应
  • 机器学习(决策树)
  • pytest的前置与后置
  • 决策树:机器学习中的直观分类与回归工具
  • CPTS---Active 复现
  • Python netifaces 库详解:跨平台网络接口与 IP 地址管理
  • Alma Linux 8 中解决掉 Failed to set locale, defaulting to C.UTF-8
  • vue3入门-v-model、ref和reactive讲解
  • Flink Stream API - 源码开发需求描述
  • Apache IoTDB集群部署实战:1C2D架构的高性能时序数据库搭建与优化指南
  • Claude Code 代理商汇总:2025年最新评测
  • 【Vivado TCL教程】从零开始掌握Xilinx Vivado TCL脚本编程(一)
  • 【Jenkins】03 - 自动构建和docker构建
  • 在 CentOS 7 上使用 LAMP 架构部署 WordPress
  • 制作全流程对比:侗家灰水粽VS布依族草灰粽的8道工序差异
  • 大厂 | 华为半导体业务部2026届秋招启动
  • scikit-learn/sklearn学习|弹性网络ElasticNet解读
  • 机器学习-决策树:从原理到实战的机器学习入门指南
  • 大模型的底层运算线性代数
  • 实现自学习系统,输入excel文件,能学习后进行相应回答
  • 香港服务器是否支持RAID磁盘阵列?
  • RTSP 播放器 vs RTMP 播放器:延迟对比与技术实践
  • HAProxy使用方法以及和LVS区别
  • 【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
  • NY291NY297美光固态闪存NY298NY303
  • Jina Embeddings:高性能多模态向量模型的演进之路
  • Minitab AI 加持的头脑风暴法,破解企业改进难题
  • 驱动开发系列64 - glCompileShader实现之 GLSL normalize 精度优化
  • Linux 中断机制深度分析