解决假XLS文件头错误

25 阅读4分钟

解决假XLS文件头错误:降级Python环境实现真·XLS格式导出

本文记录一次真实业务排障全过程:后缀是.xls、实际是CSV、文件头错误、必须输出标准XLS、新版Python无法写入的终极解决方案。适合处理旧系统、legacy工具、必须兼容Excel 97-2003格式的场景。


一、问题背景

业务场景需要批量处理一批表格文件:

  • 文件后缀:.xls
  • 来源:旧系统导出
  • 问题:无法被正常Excel库解析,列错位、乱码
  • 强制要求:输出必须是真·XLS格式(后续工具只认XLS,不支持XLSX)
  • 开发环境:Windows / MacOS + Python

二、关键报错:直接暴露真相

最开始用常规方式读取.xls时,直接报错:

xlrd.biffh.XLRDError: Unsupported format, or corrupt file: Expected BOF record; found b'"\xd3\xc3\xbb\xa7\xb1\xe0\xba'

报错解读

  • Expected BOF record真正的XLS必须以BOF文件头开头
  • found b'...中文...':文件内容实际是GBK文本

结论

这是假XLSCSV文本文件 → 改后缀为.xls → 骗过Excel,但骗不过文件头校验


三、中间踩过的所有坑(避坑必读)

1. 用pandas直接读取CSV

能读,但出现:

  • 列全部挤在第二列
  • 行首空列、首行偏移
  • 分隔符识别失败

2. 导出XLSX再改后缀为XLS

完全不可用!

  • XLSX = ZIP压缩包格式
  • XLS = BIFF二进制格式
  • 改后缀只是自欺欺人
  • 文件头依旧错误,后续工具直接拒绝读取

3. 新版Python无法写XLS

Python 3.10+ / pandas 1.4+ 之后:

  • 移除xlwt支持
  • 不再支持写入.xls
  • 无论怎么装库都会报错:
    ValueError: No Excel writer 'xlwt'
    

四、问题真正症结(一句话总结)

  1. 源文件:假XLS,真CSV(无合法Excel文件头)
  2. 目标格式:真·XLS(Excel 97-2003,必须带BOF头)
  3. 现代Python生态:已彻底放弃XLS写入
  4. 唯一出路:降级环境 + 使用xlwt

五、最终解决方案:固定版本环境

这是纯代码、不依赖Excel软件、可打包EXE分发的唯一可行方案。

固定版本组合

Python 3.8          (最后支持xlwt的稳定版)
pandas 1.3.5        最后支持xlwt引擎
xlwt 1.3.0          唯一能生成真XLS的库
xlrd 1.2.0

安装命令

pip install pandas==1.3.5 xlwt==1.3.0 xlrd==1.2.0 openpyxl

六、最终可用脚本(可直接生产使用)

功能:

  • 接收目录参数,批量处理
  • 自动修复:列错位、空列、中文乱码、首行偏移
  • 输出标准XLS(带正确BOF文件头)
  • 不修改原文件、不保留中间文件
# -*- coding: utf-8 -*-
import sys
import os
import pandas as pd

def convert_to_real_xls(file_path):
    base, ext = os.path.splitext(file_path)
    output_file = base + ".xls"

    try:
        # 以CSV方式读取假XLS文件
        df = pd.read_csv(
            file_path,
            encoding="gbk",
            sep=None,
            engine="python",
            on_bad_lines="skip",
            skip_blank_lines=True
        )

        # 清理空列、错列
        df = df.dropna(how="all", axis=1)
        df = df.loc[:, ~df.columns.str.contains("^Unnamed")]

        # 写入真正的XLS(正确文件头)
        df.to_excel(
            output_file,
            index=False,
            engine="xlwt"
        )
        print(f"✅ 转换成功:{output_file}")

    except Exception as e:
        print(f"❌ 处理失败:{file_path} => {str(e)}")

def process_folder(folder_path):
    if not os.path.isdir(folder_path):
        print(f"❌ 目录不存在:{folder_path}")
        return

    file_list = [f for f in os.listdir(folder_path) if f.lower().endswith(".xls")]
    if not file_list:
        print("ℹ️ 当前目录无 xls 文件")
        return

    print(f"📂 找到 {len(file_list)} 个文件,开始修复...\n")
    for filename in file_list:
        full_path = os.path.join(folder_path, filename)
        convert_to_real_xls(full_path)

if __name__ == "__main__":
    print("=== XLS 假文件修复工具(输出真·XLS格式) ===")
    if len(sys.argv) < 2:
        # print("使用方法:python fix_xls.py 目录路径")
        # print("示例:python fix_xls.py .")
        print("使用方法:cvt.exe 目录路径")
        print("示例:evt.exe .")
        sys.exit(1)
    process_folder(sys.argv[1])

七、运行方式

python fix_xls.py .

八、验证是否为“真XLS”

用记事本/十六进制工具打开生成的.xls前4字节必须是:

D0 CF 11 E0

这是 Excel 97-2003 标准BOF文件头,所有旧系统/工具均可识别。


九、打包成EXE分发(Windows)

使用 pyinstaller

pip install pyinstaller
pyinstaller --onefile --console --clean --name cvt.exe fix_xls.py

生成路径:

dist/cvt.exe

可直接在无Python环境的Windows上运行。

⚠️:在 MacOS 上打包后在 Window 上打包会出现 ""


十、核心经验总结(干货)

  1. 文件后缀不可信,文件头才是唯一标识
  2. CSV改后缀 ≠ XLS,Excel能打开是因为容错,不是格式合法
  3. XLS 与 XLSX 是完全不同的格式,不能靠改后缀兼容
  4. 现代Python无法写XLS,必须降级到Python3.8 + xlwt
  5. 真XLS标志:文件头 D0 CF 11 E0
  6. 旧系统依赖XLS是常态,降级环境是最稳定、最可移植的方案

十一、适用场景

  • 旧系统/银行/政府/legacy工具只认XLS
  • 文件为CSV改后缀导致的文件头错误
  • 需要批量修复列错位、乱码
  • 需要生成可分发EXE工具
  • Mac/Windows跨平台开发