385 lines
8.5 KiB
Markdown
385 lines
8.5 KiB
Markdown
# ExcelData 模块技术文档
|
||
|
||
## 简介
|
||
|
||
`ExcelData` 是一个基于 `xlrd` 的 Python 工具类,用于从 Excel 文件(`.xls` 或 `.xlsx`)中读取结构化数据,并将其转换为 Python 字典或列表结构。支持类型转换、嵌套结构解析、注释跳过、跨文件引用(include)等功能,适用于配置文件、数据导入等场景。
|
||
|
||
该模块特别适合将 Excel 表格用作轻量级数据库或配置管理工具。
|
||
|
||
---
|
||
|
||
## 依赖
|
||
|
||
- `xlrd`:用于读取 Excel 文件
|
||
- `datetime`:处理日期类型单元格
|
||
- `os`, `sys`:系统相关操作
|
||
- `appPublic.strUtils.lrtrim`:字符串去首尾空白(需确保此模块存在)
|
||
|
||
> ⚠️ 注意:`appPublic.strUtils` 是外部自定义模块,必须在项目路径中可用。
|
||
|
||
---
|
||
|
||
## 核心功能
|
||
|
||
- 自动识别并跳过注释行(以 `#` 开头)
|
||
- 支持多种数据结构标记(如 `__dict__`, `__list__`, `__include__`)
|
||
- 支持字段类型自动转换(`int`, `float`, `str`, `list`)
|
||
- 支持多表加载与嵌套结构解析
|
||
- 支持跨 Excel 文件包含(include)
|
||
- 可扩展性强,便于集成到其他系统中
|
||
|
||
---
|
||
|
||
## 全局变量说明
|
||
|
||
### `TCS` 类型映射表
|
||
|
||
```python
|
||
TCS = {
|
||
'int': int,
|
||
'float': float,
|
||
'str': str,
|
||
}
|
||
```
|
||
|
||
用于定义支持的类型转换标识符及其对应的 Python 类型。
|
||
|
||
---
|
||
|
||
## 辅助函数
|
||
|
||
### `isEmptyCell(cell) → bool`
|
||
|
||
判断某个单元格是否为空。
|
||
|
||
**参数:**
|
||
- `cell`: xlrd 单元格对象
|
||
|
||
**返回值:**
|
||
- `True` 如果是空单元格,否则 `False`
|
||
|
||
---
|
||
|
||
### `isCommentValue(v) → bool`
|
||
|
||
判断某值是否为注释(即字符串且以 `#` 开头)。
|
||
|
||
**参数:**
|
||
- `v`: 任意值
|
||
|
||
**返回值:**
|
||
- `True` 如果是注释,否则 `False`
|
||
|
||
---
|
||
|
||
### `purekey(k) → str`
|
||
|
||
提取键名中的实际名称,去除冒号后的类型/指令信息。
|
||
|
||
例如:
|
||
```python
|
||
purekey("name:int") # 返回 "name"
|
||
purekey("age") # 返回 "age"
|
||
```
|
||
|
||
**参数:**
|
||
- `k` (str): 原始键名
|
||
|
||
**返回值:**
|
||
- 不含类型修饰的纯键名
|
||
|
||
---
|
||
|
||
### `castedValue(v, k) → any`
|
||
|
||
根据键名中的类型标识对值进行类型转换或结构处理。
|
||
|
||
**参数:**
|
||
- `v`: 待处理的值
|
||
- `k`: 键名(可能包含类型信息,如 `:int`, `:list`)
|
||
|
||
**支持的操作:**
|
||
- `:int`, `:float`, `:str`:执行相应类型转换
|
||
- `:list`:将字符串按逗号分割,或包装非列表为列表
|
||
- 多重标签支持(如 `key:list:int` 表示转为整数列表)
|
||
|
||
**返回值:**
|
||
- 转换后的值
|
||
|
||
**示例:**
|
||
|
||
| 输入 (`v`, `k`) | 输出 |
|
||
|------------------|------|
|
||
| `"123"`, `"age:int"` | `123` (int) |
|
||
| `"1.5,2.7"`, `"values:list:float"` | `[1.5, 2.7]` |
|
||
| `42`, `"tags:list"` | `[42]` |
|
||
|
||
---
|
||
|
||
## 主要类:`ExcelData`
|
||
|
||
### 构造函数 `__init__(xlsfile, encoding='UTF8', startrow=0, startcol=0)`
|
||
|
||
初始化 Excel 数据读取器。
|
||
|
||
**参数:**
|
||
- `xlsfile` (str): Excel 文件路径
|
||
- `encoding` (str): 文本编码,默认 `'UTF8'`
|
||
- `startrow` (int): 起始行索引(未使用,固定为 0)
|
||
- `startcol` (int): 起始列索引(未使用,固定为 0)
|
||
|
||
**行为:**
|
||
- 打开 Excel 文件
|
||
- 调用 `dataload()` 加载所有工作表数据
|
||
- 存储于 `self._dataset`
|
||
|
||
---
|
||
|
||
### 属性与方法
|
||
|
||
#### `cellvalue(sheet, x, y) → any`
|
||
|
||
获取指定位置单元格的值,并做必要处理。
|
||
|
||
**参数:**
|
||
- `sheet`: xlrd sheet 对象
|
||
- `x`, `y`: 行列索引(从 0 开始)
|
||
|
||
**返回值:**
|
||
- `None`:空单元格
|
||
- `datetime.date`:日期类型
|
||
- 处理过的字符串(去空格、编码转换)
|
||
|
||
---
|
||
|
||
#### `isCommentCell(cell) → bool`
|
||
|
||
判断单元格是否为注释。
|
||
|
||
**逻辑:**
|
||
- 非空
|
||
- 内容是字符串且以 `#` 开头
|
||
|
||
---
|
||
|
||
#### `dateMode() → int`
|
||
|
||
返回 Excel 的日期模式(`0` 表示 1900 基准,`1` 表示 1904 基准),由 `xlrd` 提供。
|
||
|
||
---
|
||
|
||
#### `trimedValue(v) → any`
|
||
|
||
对值进行清理:
|
||
- 统一编码为指定格式(默认 UTF-8)
|
||
- 使用 `lrtrim` 去除首尾空白字符
|
||
|
||
---
|
||
|
||
#### `dataload() → dict`
|
||
|
||
加载整个 Excel 文件的所有工作表。
|
||
|
||
**返回值:**
|
||
- 字典,键为工作表名(已 trim),值为调用 `loadSheetData(sheet)` 的结果
|
||
|
||
---
|
||
|
||
#### `findDataRange(sheet, pos, maxr) → int`
|
||
|
||
查找当前数据块的最大有效行号。
|
||
|
||
**用途:**
|
||
- 在字典结构中确定子结构边界
|
||
|
||
**参数:**
|
||
- `sheet`: 当前工作表
|
||
- `pos`: 起始坐标 `(x, y)`
|
||
- `maxr`: 最大行限制
|
||
|
||
**返回值:**
|
||
- 第一个非空行的行号(向下扫描),用于分段解析
|
||
|
||
---
|
||
|
||
#### `loadSheetData(sheet) → any`
|
||
|
||
加载单个工作表的数据,调用 `loadSheetDataRange(...)`。
|
||
|
||
---
|
||
|
||
#### `include(filename, id) → any`
|
||
|
||
从另一个 Excel 文件中包含数据。
|
||
|
||
**参数:**
|
||
- `filename`: 要包含的 Excel 文件路径
|
||
- `id`: 访问路径,形如 `['sheet'][key]`,支持点式访问
|
||
|
||
**实现方式:**
|
||
- 创建新的 `ExcelData` 实例
|
||
- 使用 `exec` 动态求值表达式 `data[id]`
|
||
|
||
⚠️ **注意:** 使用了 `exec`,存在安全风险,请仅用于可信文件!
|
||
|
||
---
|
||
|
||
#### `loadSingleData(sheet, pos) → any`
|
||
|
||
加载单个值或一行多个值。
|
||
|
||
**行为:**
|
||
- 若只有一列,则返回单一值
|
||
- 否则逐列读取直到遇到空或注释
|
||
- 特殊处理 `__include__` 指令
|
||
|
||
---
|
||
|
||
#### `loadDictData(sheet, pos, maxr) → dict`
|
||
|
||
以字典形式加载数据块。
|
||
|
||
**规则:**
|
||
- 每行第一列为 key(支持类型标注)
|
||
- value 从下一列开始加载
|
||
- 支持 `records:list` 标记加载记录集
|
||
- 支持递归结构
|
||
|
||
---
|
||
|
||
#### `loadSheetDataRange(sheet, pos, maxr) → any`
|
||
|
||
核心调度函数,根据首单元格内容决定如何解析数据。
|
||
|
||
**特殊关键字识别:**
|
||
| 关键字 | 含义 |
|
||
|--------|------|
|
||
| `__dict__` | 解析为字典或字典列表(支持嵌套) |
|
||
| `__list__` | 解析为列表(每行一个元素) |
|
||
| `__include__` | 包含外部文件数据 |
|
||
| 注释或空 | 忽略并继续 |
|
||
|
||
---
|
||
|
||
#### `loadRecords(sheet, pos, maxr) → list[dict]`
|
||
|
||
将表格解析为记录列表(类似 CSV)。
|
||
|
||
**流程:**
|
||
- 第一行作为字段名(可带类型)
|
||
- 后续每行为一条记录
|
||
- 忽略空列和注释列
|
||
- 使用 `castedValue` 进行类型转换
|
||
|
||
**返回值:**
|
||
- 列表,每个元素是一个字段名→值的字典
|
||
|
||
---
|
||
|
||
#### `dict() → dict`
|
||
|
||
返回已加载的全部数据。
|
||
|
||
---
|
||
|
||
## 扩展类:`ExcelDataL(ExcelData)`
|
||
|
||
继承自 `ExcelData`,区别在于 `dataload()` 方法:
|
||
|
||
- 不是以表名为键的字典
|
||
- 而是返回一个列表,每个元素是一个 `{表名: 数据}` 字典
|
||
|
||
**用途:**
|
||
- 保持工作表顺序
|
||
- 支持同名表(虽然不推荐)
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### 示例 Excel 结构
|
||
|
||
| A | B | C |
|
||
|-----------|-------------|----------|
|
||
| name:str | age:int | tags:list|
|
||
| Alice | 25 | py,dev |
|
||
| Bob | 30 | js,web |
|
||
|
||
调用后得到:
|
||
```python
|
||
{
|
||
"Sheet1": [
|
||
{"name": "Alice", "age": 25, "tags": ["py", "dev"]},
|
||
{"name": "Bob", "age": 30, "tags": ["js", "web"]}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 包含外部文件
|
||
|
||
| A | B | C |
|
||
|---------------|------------------|-------------|
|
||
| __include__ | config.xlsx | ['db']['host'] |
|
||
|
||
会尝试加载 `config.xlsx` 并提取 `data['db']['host']`
|
||
|
||
---
|
||
|
||
## 命令行运行
|
||
|
||
```bash
|
||
python exceldata.py your_file.xls
|
||
```
|
||
|
||
输出整个解析结果的 JSON 形式(通过 `print(ed.dict())`)。
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
1. **编码问题**
|
||
- 推荐使用 UTF-8 编码保存 Excel 中的文本
|
||
- 若出现乱码,请检查 `encoding` 参数
|
||
|
||
2. **安全性警告**
|
||
- `include()` 函数使用 `exec`,不要用于不可信输入
|
||
|
||
3. **版本兼容性**
|
||
- 使用 `xlrd`,注意其对 `.xlsx` 的支持有限(建议使用 `xlrd>=1.2.0` 或考虑迁移到 `openpyxl`)
|
||
|
||
4. **性能**
|
||
- 适合中小规模配置文件,不适合大数据量导入
|
||
|
||
5. **错误处理**
|
||
- 部分异常被捕获并打印,但不会中断程序
|
||
|
||
---
|
||
|
||
## 已知限制
|
||
|
||
- 仅支持读取,不支持写入
|
||
- 日期仅提取年月日,忽略时分秒
|
||
- `__dict__` 和 `__list__` 结构需要严格对齐
|
||
- `exec` 使用存在安全隐患
|
||
- `lrtrim` 来自外部模块,需确保安装
|
||
|
||
---
|
||
|
||
## 未来改进建议
|
||
|
||
1. 替换 `exec` 为更安全的路径访问机制(如 `dpath` 或递归查找)
|
||
2. 支持更多数据类型(如布尔、JSON)
|
||
3. 添加日志系统代替 `print`
|
||
4. 支持 `openpyxl` 以兼容现代 Excel 格式
|
||
5. 提供导出为 JSON/YAML 的接口
|
||
|
||
---
|
||
|
||
## 版权与许可
|
||
|
||
© 2025 作者保留所有权利。
|
||
可用于内部项目,禁止商业分发。
|
||
|
||
---
|
||
|
||
> ✅ 文档更新时间:2025年4月5日 |