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

前面研究了电子表格的xlsx、xlsbODS格式,接下来研究WPS电子表格ET格式,这个格式网上的资料很少,WPS官方说 .et 格式是兼容 Xlsx 和 Xls 的。

然而,把它后缀改为xlsx,rust_sheet插件还是无法识别,报错

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

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

sql 复制代码
mv HelloWorld.et HelloWorld.et.xls

./duckdb132

D 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)

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

3. 公式存储

cpp 复制代码
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对比

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

六、编程处理

1. 使用Python读取

python 复制代码
import xlrd

# 打开XLS文件
workbook = xlrd.open_workbook("example.xls")

# 获取第一个工作表
sheet = workbook.sheet_by_index(0)

# 读取单元格数据
print(sheet.cell_value(0, 0))  # 第一行第一列

2. 使用Python写入

python 复制代码
import xlwt

# 创建工作簿
workbook = xlwt.Workbook()
sheet = workbook.add_sheet("Sheet1")

# 写入数据
sheet.write(0, 0, "Hello World")

# 保存文件
workbook.save("output.xls")

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

python 复制代码
from olefile import OleFileIO

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

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

bash 复制代码
python3 xlrd2.py

Hello World

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

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

bash 复制代码
python3 xlrd2.py

Hello World

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

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

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

我们知道xlsx实际是zip格式,那ET也是zip格式吗,不完全是。

我用unzip工具去列出它包含的文件,结果是这样的。

bash 复制代码
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].xml
        0  2012-07-02 09:52   _rels/
      310  2012-07-02 09:52   _rels/.rels
        0  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.rels
     6432  2012-07-02 09:52   theme/theme/theme1.xml
      138  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工具去列出:

bash 复制代码
unzip -l HelloWorld.xlsx
存档文件:  HelloWorld.xlsx
  Length      Date    Time    Name
---------  ---------- -----   ----
     1293  2012-07-02 09:52   [Content_Types].xml
        0  2012-07-02 09:52   _rels/
      735  2012-07-02 09:52   _rels/.rels
        0  2012-07-02 09:52   docProps/
      564  2012-07-02 09:52   docProps/app.xml
      573  2012-07-02 09:52   docProps/core.xml
      526  2012-07-02 09:52   docProps/custom.xml
        0  2012-07-02 09:52   xl/
        0  2012-07-02 09:52   xl/_rels/
      698  2012-07-02 09:52   xl/_rels/workbook.xml.rels
      187  2012-07-02 09:52   xl/sharedStrings.xml
    20006  2012-07-02 09:52   xl/styles.xml
        0  2012-07-02 09:52   xl/theme/
     6432  2012-07-02 09:52   xl/theme/theme1.xml
      477  2012-07-02 09:52   xl/workbook.xml
        0  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注释字段(推荐)

python 复制代码
import zipfile

def 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中的偏移量
python 复制代码
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 Record
    eocd_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字段(针对特定文件)

python 复制代码
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文件完整性

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

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

高级技巧:隐藏数据

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

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

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

相关推荐
zskj_zhyl24 分钟前
家庭健康能量站:微高压氧舱结合艾灸机器人,智享双重养生SPA
人工智能·科技·安全·机器人
朗迪锋27 分钟前
数字孪生 :提高制造生产力的智能方法
大数据·人工智能·制造
网安INF40 分钟前
【论文阅读】-《HopSkipJumpAttack: A Query-Efficient Decision-Based Attack》
论文阅读·人工智能·深度学习·网络安全·对抗攻击
你知道网上冲浪吗1 小时前
【原创理论】Stochastic Coupled Dyadic System (SCDS):一个用于两性关系动力学建模的随机耦合系统框架
python·算法·数学建模·数值分析
钢铁男儿1 小时前
Python 正则表达式核心元字符全解析
python
杨荧1 小时前
基于Python的宠物服务管理系统 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python·信息可视化
CodeCraft Studio2 小时前
在 Python 中操作 Excel 文件的高效方案 —— Aspose.Cells for Python
python·ui·excel·报表·aspose·aspose.cells
plusplus1682 小时前
边缘智能实战手册:攻克IoT应用三大挑战的AI战术
人工智能·物联网
果粒橙_LGC3 小时前
论文阅读系列(一)Qwen-Image Technical Report
论文阅读·人工智能·学习