Files
pinmap-to-pinlist/Releases/v1.0.1/docs/architecture-design.md
Agent 836ad20515 v1.1.0: 增加交互提示、路径输入、窗口属性配置
- main.py: 增加show_banner()启动说明、各阶段[INFO]日志、结果摘要、任意键退出
- file_selector.py: 重写为路径输入→验证→空输入弹窗回退→不存在循环重试
- run.bat: 新建启动脚本(chcp 65001, mode con cols=80 lines=20, color 0B, title固定署名, pause)
- Code/docs/modification-assessment.md: 修改需求评估文档
2026-05-25 17:29:19 +08:00

28 KiB
Raw Blame History

PinMAP → PinList 转换器 — 全局架构设计

版本: v1.0
日期: 2026-05-25
架构师: 脚本架构师 (Script Architect)
状态: 待审批


1. 项目概述

1.1 背景

将 Excel 格式的 PinMAP 文件(方形封装引脚布局图)自动转换为 PinList 格式(引脚序号列表),消除手动抄录的低效与错误风险。

1.2 核心规则

  • PinMAP 为方形/长方形结构,引脚沿四条边分布,左上角为 1 脚,逆时针排序
  • 左上角 = 1 脚,右上角 = 上边最后一个脚,右下角 = 下边最后一个脚,左下角 = 左边最后一个脚
  • 四个角点被相邻两边共享(不重复计数)

1.3 约束

约束项 说明
运行平台 Windows
技术栈 Python 标准库,零第三方依赖
输入格式 .xls(必须支持)、.xlsx(优先支持)
输出格式 .xlsx
交互方式 命令行 + 文件选择对话框

2. 技术选型

2.1 为什么不用 openpyxl / xlrd

项目明确要求 无第三方依赖,因此必须使用 Python 标准库自行实现 Excel 读写。

2.2 XLS 读取 — BIFF8 二进制解析

.xls 是 Microsoft BIFF8 二进制格式(复合文档 OLE2 容器)。

实现策略

1. 使用 struct 模块解析 OLE2 复合文档头
2. 解析 FAT文件分配表定位 MiniFAT 和目录流
3. 定位 Workbook 流,读取 BIFF8 记录序列
4. 关键记录类型:
   - 0x0009 (BOF)        → 块起始标记
   - 0x00FD (LABELSST)   → 共享字符串表中的文本单元格
   - 0x0006 (FORMULA)    → 公式/数值单元格
   - 0x0203 (NUMBER)     → 数值单元格
   - 0x000C (RK)         → RK 数值(压缩整数/浮点)
   - 0x000D (RString)    → 内联字符串
   - 0x00FC (STRING)     → 字符串结果
   - 0x0034 (SST)        → 全局共享字符串表
   - 0x0042 (BOUNDSHEET) → 工作表信息
5. 提取每个单元格的 (行, 列, 值) 三元组

复杂度评估中等。BIFF8 是固定长度记录流struct 解析直接。需处理 Unicode 编码BIFF8 默认 UTF-16LE部分兼容 ASCII

2.3 XLSX 读取 — ZIP + XML

.xlsx 本质是 ZIP 压缩包,内部为 Office Open XML (OOXML)。

实现策略

import zipfile
import xml.etree.ElementTree as ET

1. zipfile.ZipFile 打开 .xlsx
2. 读取 [Content_Types].xml 确认结构
3. 读取 xl/workbook.xml 获取工作表关系
4. 读取 xl/worksheets/sheet1.xml 获取单元格数据
5. 读取 xl/sharedStrings.xml 获取共享字符串表
6. 解析单元格坐标 "A2"  列A行2和值类型

复杂度评估低。zipfile 和 xml.etree 均为标准库XML 结构规范清晰。

2.4 XLSX 写入 — ZIP + XML 生成

实现策略

import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO

1. 构建 OOXML 目录结构
   [Content_Types].xml
   _rels/.rels
   xl/workbook.xml
   xl/worksheets/sheet1.xml
   xl/sharedStrings.xml
   xl/_rels/workbook.xml.rels

2. 使用 zipfile.ZipFile 写入ZIP_DEFLATED

3. 关键 XML 构建
   - sharedStrings.xml: 所有唯一字符串的 SST
   - sheet1.xml: 单元格坐标 + si (SST index) 引用
   - workbook.xml: 工作表引用
   - [Content_Types].xml: MIME 类型声明

复杂度评估低。XML 结构固定,模板化生成即可。

2.5 技术选型总结

操作 格式 标准库模块 难度
读取 xls struct + 手动 OLE2/BIFF8 解析
读取 xlsx zipfile + xml.etree.ElementTree
写入 xlsx zipfile + xml.etree.ElementTree
文件选择 tkinter.filedialog

3. 模块划分

pinmap-to-pinlist/
├── Code/
│   ├── src/
│   │   ├── main.py              # 入口:流程编排
│   │   ├── file_selector.py     # 模块1文件选择
│   │   ├── xls_reader.py        # 模块2aXLS 解析引擎
│   │   ├── xlsx_reader.py       # 模块2bXLSX 解析引擎
│   │   ├── pinmap_parser.py     # 模块3PinMAP 结构解析
│   │   ├── validator.py         # 模块4数据验证
│   │   ├── pinlist_generator.py # 模块5PinList 生成
│   │   └── xlsx_writer.py       # 模块6XLSX 输出引擎
│   └── docs/
│       └── architecture-design.md
├── Test/
└── Releases/

3.1 模块职责

模块1file_selector — 文件选择

def select_file() -> str | None:
    """弹出文件选择对话框,返回选中文件路径或 None取消"""
  • 使用 tkinter.filedialog.askopenfilename
  • 文件类型过滤:*.xls;*.xlsx
  • 无 GUI 环境时回退到命令行参数

模块2axls_reader — XLS 解析引擎

class XLSReader:
    def __init__(self, filepath: str)
    def read_all_cells(self) -> dict[tuple[int, int], str]:
        """返回 {(row, col): value} 字典,行列从 0 开始"""
    def close(self)

内部结构

XLSReader
├── OLE2Parser      → 解析复合文档,定位 Workbook 流
├── BIFF8Parser     → 解析 BIFF8 记录流
│   ├── SSTParser   → 共享字符串表
│   └── CellParser  → 单元格记录
└── CellMap         → 组装为 (row, col) → value 映射

模块2bxlsx_reader — XLSX 解析引擎

class XLSXReader:
    def __init__(self, filepath: str)
    def read_all_cells(self) -> dict[tuple[int, int], str]:
        """返回 {(row, col): value} 字典,行列从 0 开始"""
    def close(self)

内部结构

XLSXReader
├── ZipExtractor    → 解压 .xlsx 到内存
├── SharedStrings   → 解析 sharedStrings.xml
├── SheetParser     → 解析 sheet1.xml
│   ├── CoordParser → 列字母转索引 (A→0, B→1, ...)
│   └── CellParser  → 提取单元格值
└── CellMap         → 组装为 (row, col) → value 映射

模块3pinmap_parser — PinMAP 结构解析

def parse_pinmap(cells: dict[tuple[int, int], str]) -> PinMAP:
    """
    解析步骤:
    1. 排除 (0,0) 后扫描非空单元格,确定方形边界
    2. 提取 A1 封装信息
    3. 沿四条边提取引脚序号(边界单元格)和 PinName相邻内侧单元格
    4. 逆时针遍历(左→下→右→上),按单元格位置去重(角点共享)
    5. 返回 PinMAP 对象
    """

解析算法

Step 1: 确定方形边界
  - 排除 (0,0)(封装信息单元格)
  - 扫描所有非空单元格,找到最小/最大行号和列号
  - width = max_col - min_col + 1
  - height = max_row - min_row + 1
  - 验证width >= 2 且 height >= 2

Step 2: 提取 A1 封装信息
  - cells[(0, 0)] → package_info

Step 3: 构建 PinName 查找表
  每条边的 PinName 位于序号单元格的"内侧相邻"位置:
    左边:序号在 (r, min_col)        Name 在 (r, min_col+1)
    下边:序号在 (max_row, c)        Name 在 (max_row-1, c)
    右边:序号在 (r, max_col)        Name 在 (r, max_col-1)
    上边:序号在 (min_row, c)        Name 在 (min_row+1, c)

Step 4: 逆时针遍历四条边(按单元格位置去重)
  4a. 左边:从上到下 (row: min_row → max_row, col: min_col)
  4b. 下边:从左到右 (row: max_row, col: min_col+1 → max_col)
  4c. 右边:从下到上 (row: max_row-1 → min_row, col: max_col)
  4d. 上边:从右到左 (row: min_row, col: max_col-1 → min_col)
  
  角点去重:按 (row, col) 单元格位置去重,而非按 Pin 序号。
  这样如果两个不同单元格恰好有相同序号validator 能检测到。

Step 5: 组装 Pin 列表
  按逆时针顺序Pin1(左上角) → Pin2 → ... → PinN

模块4validator — 数据验证

def validate_pinmap(pinmap: PinMAP) -> ValidationResult:
    """
    验证项:
    1. Pin序号唯一性无重复
    2. Pin序号连续性1..N 无间隔)
    3. PinName 缺失检测warning默认 NC
    4. 方形结构完整性width/height >= 2
    """

模块5pinlist_generator — PinList 生成

class PinListGenerator:
    def __init__(self, pinmap: PinMAP, validation: ValidationResult)
    def generate(self) -> PinList:
        """
        生成规则:
        - A1 = 封装信息
        - A列 = PinName
        - B列 = Pin序号
        - 按 Pin序号 递增排序
        """

模块6xlsx_writer — XLSX 输出引擎

class XLSXWriter:
    def __init__(self)
    def write_pinlist(self, pinlist: PinList, output_path: str)

3.2 模块依赖关系

main.py
  ├── file_selector.py
  ├── xls_reader.py ──┐
  ├── xlsx_reader.py ─┤
  │                   ▼
  │            pinmap_parser.py
  │                   ▼
  │             validator.py
  │                   ▼
  │        pinlist_generator.py
  │                   ▼
  └─────────── xlsx_writer.py

4. 数据结构设计

4.1 Pin引脚

@dataclass
class Pin:
    number: int          # 引脚序号1-based
    name: str            # 引脚名称(缺失时默认为 "NC"
    edge: str            # 所在边: "top" | "right" | "bottom" | "left"
    position_on_edge: int  # 在该边上的位置0-based

4.2 PinMAP引脚映射图

@dataclass
class PinMAP:
    package_info: str                    # A1 单元格封装信息
    pins: list[Pin]                      # 所有引脚(按序号排序)
    width: int                           # 方形宽度(列数)
    height: int                          # 方形高度(行数)
    grid_origin: tuple[int, int]         # (row, col) 方形左上角
    raw_cells: dict[tuple[int, int], str]  # 原始单元格数据(调试用)

4.3 PinList引脚列表

@dataclass
class PinList:
    package_info: str                    # 输出 A1 单元格
    rows: list[tuple[str, int]]          # [(PinName, Pin序号), ...] 按序号排序

4.4 ValidationResult验证结果

@dataclass
class ValidationError:
    level: str       # "error" | "warning"
    message: str     # 错误描述
    details: str     # 详细信息如重复的序号、缺失的Pin等

@dataclass
class ValidationResult:
    is_valid: bool
    errors: list[ValidationError]
    warnings: list[ValidationError]

4.5 内部:单元格坐标体系

统一使用 (row, col) 元组0-based
  - row 0 = Excel 第1行
  - col 0 = Excel A列
  - A1 = (0, 0)
  - A2 = (1, 0)
  - C2 = (1, 2)
  - B4 = (3, 1)

5. 异常处理策略

5.1 异常分类

级别 类型 处理方式 示例
FATAL 文件格式错误 终止 + 错误信息 非Excel文件、BIFF损坏
FATAL 结构错误 终止 + 错误信息 非方形、缺少A1、无边数据
ERROR 数据错误 终止 + 详细错误 序号不连续、序号重复
WARN 数据警告 提示 + 继续 PinName缺失默认NC
INFO 信息提示 仅显示 转换完成、统计信息

5.2 自定义异常层次

class PinMapError(Exception):
    """基类异常"""

class FileFormatError(PinMapError):
    """文件格式错误非xls/xlsx、文件损坏"""

class StructureError(PinMapError):
    """PinMAP结构错误非方形、缺少必要数据"""

class ValidationError(PinMapError):
    """数据验证错误(序号不连续、重复)"""

class WarningLevel(PinMapError):
    """警告级别PinName缺失等可继续处理"""

5.3 错误信息规范

[级别] 错误类别: 具体描述
  详细信息: ...
  建议操作: ...

示例:

[ERROR] 序号不连续: 检测到序号间断
  预期: 1,2,3,4,5,6  实际: 1,2,3,5,6
  缺失序号: 4
  建议: 检查PinMAP文件中是否有遗漏的引脚

[WARN] PinName缺失: 检测到 3 个引脚缺少PinName
  缺失引脚: Pin 7, Pin 12, Pin 18
  处理: 已自动设为 "NC"

5.4 主流程异常处理

def main():
    try:
        filepath = select_file()
        if not filepath:
            return  # 用户取消

        cells = read_excel(filepath)
        pinmap = parse_pinmap(cells)
        result = validate(pinmap)

        if result.has_errors():
            print_errors(result.errors)
            return

        if result.has_warnings():
            print_warnings(result.warnings)
            if not confirm_continue():
                return

        pinlist = generate(pinmap, result)
        output_path = build_output_path(filepath)
        write_xlsx(pinlist, output_path)
        print_success(output_path)

    except FileFormatError as e:
        print_fatal(f"文件格式错误: {e}")
    except StructureError as e:
        print_fatal(f"结构错误: {e}")
    except ValidationError as e:
        print_fatal(f"数据验证失败: {e}")
    except Exception as e:
        print_fatal(f"未知错误: {e}")

6. 文件处理流程图

┌─────────────────────────────────────────────────────────────────┐
│                        主流程 (main.py)                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              ┌───────────────────────────┐
              │  1. 文件选择 (file_selector) │
              │  - tkinter 文件对话框      │
              │  - 过滤 *.xls, *.xlsx     │
              └───────────┬───────────────┘
                          │
              ┌───────────▼───────────────┐
              │  2. 读取 Excel 文件        │
              │  ┌─────────────────────┐  │
              │  │ 判断文件格式        │  │
              │  │ .xls → xls_reader  │  │
              │  │ .xlsx → xlsx_reader│  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ 解析为单元格字典    │  │
              │  │ {(row,col): value}  │  │
              │  └─────────┬───────────┘  │
              └────────────┬──────────────┘
                           │
              ┌────────────▼──────────────┐
              │  3. PinMAP 解析 (pinmap_parser) │
              │  ┌─────────────────────┐  │
              │  │ ① 定位方形边界     │  │
              │  │    扫描非空单元格   │  │
              │  │    确定 width/height│  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ ② 提取 A1 封装信息  │  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ ③ 沿四边提取引脚    │  │
              │  │    上边 → 右边      │  │
              │  │    下边 → 左边      │  │
              │  │    逆时针排序       │  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ ④ 组装 PinMAP 对象  │  │
              │  └─────────┬───────────┘  │
              └────────────┬──────────────┘
                           │
              ┌────────────▼──────────────┐
              │  4. 数据验证 (validator)   │
              │  ┌─────────────────────┐  │
              │  │ ✓ 序号连续性检查    │  │
              │  │ ✓ 序号唯一性检查    │  │
              │  │ ✓ PinName 缺失检查  │  │
              │  │ ✓ 方形结构完整性    │  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ ERROR → 终止流程    │  │
              │  │ WARN  → 提示确认    │  │
              │  └─────────┬───────────┘  │
              └────────────┬──────────────┘
                           │
              ┌────────────▼──────────────┐
              │  5. PinList 生成 (generator) │
              │  ┌─────────────────────┐  │
              │  │ A1 = 封装信息       │  │
              │  │ A列 = PinName       │  │
              │  │ B列 = Pin序号       │  │
              │  │ 按序号递增排序      │  │
              │  └─────────┬───────────┘  │
              └────────────┬──────────────┘
                           │
              ┌────────────▼──────────────┐
              │  6. XLSX 输出 (xlsx_writer) │
              │  ┌─────────────────────┐  │
              │  │ 构建 OOXML 结构     │  │
              │  │ [Content_Types].xml │  │
              │  │ xl/workbook.xml     │  │
              │  │ xl/sharedStrings.xml│  │
              │  │ xl/worksheets/      │  │
              │  │   sheet1.xml        │  │
              │  └─────────┬───────────┘  │
              │  ┌─────────▼───────────┐  │
              │  │ ZIP 打包输出        │  │
              │  └─────────┬───────────┘  │
              └────────────┬──────────────┘
                           │
                           ▼
              ┌───────────────────────────┐
              │  完成!输出 .xlsx 文件     │
              │  默认命名: {原文件名}_PinList.xlsx │
              └───────────────────────────┘

7. PinMAP 结构详解

7.1 坐标映射

以 4×4 方形为例width=4, height=4

        col A(0)   col B(1)   col C(2)   col D(3)
row 0   [A1=封装]  [PinName]  [PinName]  [PinName]    ← 上边PinName行
row 1   [1]        [2]        [3]        [4]          ← 上边Pin序号行
row 2   [PinName]  [          ]          [PinName]    ← 中间区域(留空)
row 3   [PinName]  [          ]          [PinName]    ← 中间区域(留空)
row 4   [13]       [12]       [11]       [10]         ← 下边Pin序号行
        [PinName]  [PinName]  [PinName]  [PinName]    ← 下边PinName行行5
        ↑          ↑          ↑          ↑
     左边        左边        右边        右边
     PinName    PinName    PinName    PinName
     (列前)     (列前)     (列后)     (列后)

实际引脚分布

  • 上边Pin 1(A,row1) → Pin 2(B,row1) → Pin 3(C,row1) → Pin 4(D,row1)
  • 右边Pin 5(D,row2) → Pin 6(D,row3) → Pin 7(D,row4)
  • 下边Pin 8(D,row4) ... 等等

等等,让我重新理清。根据需求描述:

C2 是上边最后一个Pin序号C3是对应PinName
A4 是左边第一个Pin序号B4是对应PinName

这说明:

  • 行1Excel第2行= 上边Pin序号行
  • 行2Excel第3行= 上边PinName行
  • 行3Excel第4行= 左边第一个Pin序号行

所以方形区域从 row=1 开始Excel第2行row=0 是PinName行。

7.2 四边提取规则(修正版)

设方形区域:行范围 [r_top, r_bottom],列范围 [c_left, c_right]

上边 (Top Edge)
  Pin序号位置row=r_top, col=c_left → c_right从左到右
  PinName位置row=r_top-1, col=c_left → c_right

右边 (Right Edge)
  Pin序号位置col=c_right, row=r_top → r_bottom从上到下
  PinName位置col=c_right+1, row=r_top → r_bottom

下边 (Bottom Edge)
  Pin序号位置row=r_bottom, col=c_right → c_left从右到左
  PinName位置row=r_bottom+1, col=c_right → c_left

左边 (Left Edge)
  Pin序号位置col=c_left, row=r_bottom → r_top从下到上
  PinName位置col=c_left-1, row=r_bottom → r_top即B列当c_left=0时

7.3 角点共享规则

左上角 (c_left, r_top)     = 上边起点 = 左边终点 → Pin 1
右上角 (c_right, r_top)    = 上边终点 = 右边起点
右下角 (c_right, r_bottom) = 右边终点 = 下边起点
左下角 (c_left, r_bottom)  = 下边终点 = 左边终点

总Pin数 = 2 × width + 2 × height - 4

7.4 长方形支持

非正方形示例width=6, height=4

总Pin数 = 2×6 + 2×4 - 4 = 16

上边6个引脚1-6
右边3个引脚7-9
下边5个引脚10-14
左边3个引脚15-16回到Pin 1

验证6 + 3 + 5 + 2 = 16 ✓(左边排除两个角点)

8. 任务拆分建议

8.1 推荐拆分方案

建议拆分为 3 个子任务,由 2-3 个编码 Agent 并行开发:

任务 AExcel 读写引擎(最复杂,优先开发)

负责模块xls_reader.py, xlsx_reader.py, xlsx_writer.py

工作内容

  1. 实现 BIFF8 OLE2 解析器xls 读取)
  2. 实现 ZIP+XML 解析器xlsx 读取)
  3. 实现 OOXML 生成器xlsx 写入)
  4. 统一接口:read_excel(filepath) → dict[(row,col), str]
  5. 编写单元测试(用已知 xls/xlsx 文件验证)

预估工作量BIFF8 解析是最大难点)

关键风险

  • BIFF8 变体多BIFF5/BIFF8 混用、不同 Unicode 编码)
  • 需要大量测试文件验证

任务 BPinMAP 解析与验证(核心业务逻辑)

负责模块pinmap_parser.py, validator.py

工作内容

  1. 实现方形边界检测算法
  2. 实现四边引脚提取逻辑
  3. 实现角点共享处理
  4. 实现验证规则(连续性、唯一性、完整性)
  5. 编写单元测试(模拟各种 PinMAP 布局)

预估工作量:中

关键风险

  • 边界条件处理(长方形 vs 正方形、最小尺寸)
  • 角点共享逻辑的正确性

任务 C流程编排与输出集成层

负责模块main.py, file_selector.py, pinlist_generator.py

工作内容

  1. 实现文件选择对话框
  2. 实现 PinList 数据转换
  3. 实现输出文件命名和保存
  4. 实现主流程异常处理和用户提示
  5. 端到端集成测试

预估工作量:低

关键风险

  • tkinter 在 Windows 上的兼容性
  • 用户交互流程的友好性

8.2 开发顺序

第1轮任务 AExcel 读写引擎)
  ↓ 完成后
第2轮任务 BPinMAP 解析与验证)
  ↓ 完成后
第3轮任务 C流程编排与输出
  ↓ 完成后
集成测试 → 发布

8.3 接口契约(模块间约定)

# xls_reader / xlsx_reader 统一接口
def read_excel_cells(filepath: str) -> dict[tuple[int, int], str]:
    """
    输入: Excel 文件路径
    输出: {(row, col): str} 单元格字典
    约定: row/col 从 0 开始,所有值转为 str
    """

# pinmap_parser 接口
def parse_pinmap(cells: dict[tuple[int, int], str]) -> PinMAP:
    """
    输入: 单元格字典
    输出: PinMAP 对象
    约定: 结构错误时抛出 StructureError
    """

# validator 接口
def validate_pinmap(pinmap: PinMAP) -> ValidationResult:
    """
    输入: PinMAP 对象
    输出: ValidationResult
    约定: 不抛出异常,所有问题记录在 ValidationResult 中
    """

# pinlist_generator 接口
def generate_pinlist(pinmap: PinMAP, validation: ValidationResult) -> PinList:
    """
    输入: PinMAP + ValidationResult
    输出: PinList 对象
    约定: 自动处理 WARN 级别的缺失 PinName设为 NC
    """

# xlsx_writer 接口
def write_pinlist_xlsx(pinlist: PinList, output_path: str):
    """
    输入: PinList + 输出路径
    输出: 无(写入文件)
    约定: 自动创建父目录
    """

9. 项目目录结构

pinmap-to-pinlist/
├── Code/
│   ├── src/
│   │   ├── __init__.py
│   │   ├── main.py                  # 入口点
│   │   ├── file_selector.py         # 文件选择
│   │   ├── xls_reader.py            # XLS 读取引擎
│   │   ├── xlsx_reader.py           # XLSX 读取引擎
│   │   ├── pinmap_parser.py         # PinMAP 解析
│   │   ├── validator.py             # 数据验证
│   │   ├── pinlist_generator.py     # PinList 生成
│   │   ├── xlsx_writer.py           # XLSX 写入引擎
│   │   └── models.py                # 数据模型定义
│   └── docs/
│       └── architecture-design.md   # 本文档
├── Test/
│   ├── fixtures/                    # 测试用 Excel 文件
│   │   ├── sample_4x4.xls
│   │   ├── sample_4x4.xlsx
│   │   ├── sample_rect.xls
│   │   └── ...
│   └── test_*.py                    # 单元测试
└── Releases/
    └── pinmap2pinlist.exe           # 打包后的可执行文件

10. 风险与缓解

风险 影响 概率 缓解措施
BIFF8 格式变体导致解析失败 收集多种 xls 样本测试;优先实现 BIFF8 最常见子集
tkinter 在无头环境不可用 回退到命令行参数模式
xlsx 写入的 XML 结构不兼容老版本 Excel 遵循 OOXML 标准,使用最小兼容集
超大文件(>1000引脚性能问题 当前场景引脚数通常 <100无需优化

11. 附录

A. BIFF8 记录类型速查

记录码 名称 说明
0x0009 BOF 块起始
0x000A EOF 文件结束
0x00FD LABELSST 共享字符串表引用单元格
0x0203 NUMBER 浮点数单元格
0x0006 FORMULA 公式单元格
0x000C RK RK 数值
0x00FC STRING 公式字符串结果
0x0034 SST 全局共享字符串表
0x0042 BOUNDSHEET 工作表信息
0x00E0 EXTSST 扩展共享字符串表

B. OOXML xlsx 目录结构

example.xlsx (ZIP)
├── [Content_Types].xml
├── _rels/
│   └── .rels
├── xl/
│   ├── workbook.xml
│   ├── _rels/
│   │   └── workbook.xml.rels
│   ├── sharedStrings.xml
│   ├── styles.xml
│   └── worksheets/
│       ├── sheet1.xml
│       └── sheet2.xml
└── docProps/
    ├── core.xml
    └── app.xml

C. 列字母 ↔ 索引转换

def col_letter_to_index(letter: str) -> int:
    """A→0, B→1, ..., Z→25, AA→26, AB→27, ..."""
    result = 0
    for ch in letter.upper():
        result = result * 26 + (ord(ch) - ord('A') + 1)
    return result - 1

def col_index_to_letter(index: int) -> str:
    """0→A, 1→B, ..., 25→Z, 26→AA, ..."""
    result = ""
    index += 1
    while index > 0:
        index -= 1
        result = chr(index % 26 + ord('A')) + result
        index //= 26
    return result

文档结束 — 请审批后进入编码阶段