217 lines
6.4 KiB
Markdown
217 lines
6.4 KiB
Markdown
# CSVData 类技术文档
|
||
|
||
`CSVData` 是一个用于读取和处理 CSV 文件的 Python 类,提供了两种读取模式:一次性加载全部数据(`read()`)和逐行迭代处理(`iterRead()`)。该类支持自定义字段名、跳过标题行和数据起始行,适用于灵活解析各种格式的 CSV 数据。
|
||
|
||
---
|
||
|
||
## 📦 模块依赖
|
||
|
||
```python
|
||
import csv
|
||
```
|
||
|
||
> 使用标准库中的 `csv` 模块进行 CSV 文件解析。
|
||
|
||
---
|
||
|
||
## 🔧 类定义
|
||
|
||
### `class CSVData(csvfile, names=None, headline=0, dataline=1)`
|
||
|
||
#### 参数说明:
|
||
|
||
| 参数 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `csvfile` | str | 必填 | CSV 文件路径 |
|
||
| `names` | list 或 None | `None` | 自定义字段名称列表。若提供,则忽略文件中的标题行 |
|
||
| `headline` | int | `0` | 标题行所在的行号(从 0 开始),仅在 `names` 为 `None` 时使用 |
|
||
| `dataline` | int | `1` | 实际数据开始的行号(从 0 开始) |
|
||
|
||
---
|
||
|
||
## 📚 方法说明
|
||
|
||
### 1. `__init__(self, csvfile, names=None, headline=0, dataline=1)`
|
||
初始化 CSVData 实例,设置文件路径与解析参数。
|
||
|
||
---
|
||
|
||
### 2. `read(self) -> list[dict]`
|
||
以列表形式返回所有记录,每条记录是一个字典(键为字段名,值为对应列值)。
|
||
|
||
#### 返回值:
|
||
- `list[dict]`: 包含所有数据记录的列表,例如:
|
||
```python
|
||
[
|
||
{'st_date': '2024-01-01', 'open_price': '100', ...},
|
||
{'st_date': '2024-01-02', 'open_price': '102', ...},
|
||
...
|
||
]
|
||
```
|
||
|
||
#### 工作流程:
|
||
1. 打开 CSV 文件(二进制模式 `'rb'` ⚠️ 存在潜在问题,请见下方【⚠️ 注意事项】)
|
||
2. 创建 `csv.reader` 对象
|
||
3. 若未指定 `names`,则读取第 `headline` 行作为字段名
|
||
4. 从第 `dataline` 行开始逐行构建字典记录并添加到结果列表中
|
||
5. 关闭文件并返回数据
|
||
|
||
> ✅ 适合小到中等规模的数据集。
|
||
|
||
---
|
||
|
||
### 3. `iterRead(self)`
|
||
以流式方式逐行读取 CSV 文件,并触发事件回调函数。适用于大数据文件,避免内存溢出。
|
||
|
||
#### 回调机制:
|
||
- `onBegin()`: 在开始读取前调用(当前实现为空)
|
||
- `onRecord(rec)`: 每处理一行有效数据时调用
|
||
- `onFinish()`: 成功完成读取后调用
|
||
|
||
#### 异常处理:
|
||
- 使用 `try-except` 捕获异常,确保文件关闭
|
||
- 出现错误时重新抛出异常(但变量作用域有误,请见【⚠️ Bug 提示】)
|
||
|
||
> ✅ 推荐用于大型 CSV 文件处理。
|
||
|
||
---
|
||
|
||
### 4. `onBegin(self)`
|
||
钩子方法,在迭代读取开始前被调用。默认为空实现,可由子类重写。
|
||
|
||
> 当前代码中调用了 `self.onBegin()`,但该方法未定义 —— 应为 `onReadBegin`?请见下方 Bug 分析。
|
||
|
||
---
|
||
|
||
### 5. `onRecord(self, rec)`
|
||
每读取一条有效记录时调用此方法。
|
||
|
||
#### 参数:
|
||
- `rec` (dict): 当前行数据,形如 `{字段名: 值}`
|
||
|
||
#### 默认行为:
|
||
打印当前记录(`print(rec)`)
|
||
|
||
> 可继承此类并重写该方法以实现自定义逻辑(如入库、计算等)。
|
||
|
||
---
|
||
|
||
### 6. `onFinish(self)`
|
||
所有数据读取完成后调用。
|
||
|
||
#### 默认行为:
|
||
打印 `"onFinish() called"`
|
||
|
||
> 可用于资源清理或结束通知。
|
||
|
||
---
|
||
|
||
## 🖥️ 示例用法
|
||
|
||
```python
|
||
if __name__ == '__main__':
|
||
import sys
|
||
cd = CSVData(
|
||
sys.argv[1],
|
||
names=['st_date','open_price','max_price','min_price','close_price','volume','adj_price']
|
||
)
|
||
cd.iterRead()
|
||
```
|
||
|
||
### 说明:
|
||
- 从命令行传入 CSV 文件路径
|
||
- 使用自定义字段名,不依赖文件头部
|
||
- 调用 `iterRead()` 流式输出每一行数据
|
||
|
||
---
|
||
|
||
## ⚠️ 注意事项与改进建议
|
||
|
||
### ❗Bug 与潜在问题
|
||
|
||
| 问题 | 描述 | 建议修复 |
|
||
|------|------|---------|
|
||
| 1. `onBegin()` 调用错误 | 代码中调用 `self.onBegin()`,但实际定义的是 `onReadBegin()`,会导致 `AttributeError` | 将 `onReadBegin` 改名为 `onBegin`,或修正调用 |
|
||
| 2. 异常捕获语法错误 | `except exception as e:` 中 `exception` 应为 `Exception`(首字母大写) | 改为 `except Exception as e:` |
|
||
| 3. 文件打开模式冲突 | `read()` 方法使用 `'rb'` 二进制模式打开文件,但 `csv.reader` 需要文本模式 | 改为 `'r'` 并指定编码(如 `'utf-8'`) |
|
||
| 4. `fd.close()` 变量作用域错误 | 在 `except` 块中调用 `fd.close()`,但 `fd` 是实例属性应写作 `self.fd` | 改为 `self.fd.close()` |
|
||
| 5. 缺少编码设置 | 未指定字符编码,可能导致中文乱码 | 添加 `encoding='utf-8'` 参数 |
|
||
|
||
---
|
||
|
||
### ✅ 推荐修改后的 `iterRead()` 示例:
|
||
|
||
```python
|
||
def iterRead(self):
|
||
self.fd = open(self.csvfile, 'r', encoding='utf-8')
|
||
try:
|
||
reader = csv.reader(self.fd)
|
||
fields = None
|
||
if self.names is not None:
|
||
fields = self.names
|
||
lno = 0
|
||
self.onBegin() # 确保方法存在
|
||
for l in reader:
|
||
if fields is None and lno == self.headline:
|
||
fields = [f for f in l]
|
||
if lno >= self.dataline:
|
||
rec = {}
|
||
for i in range(len(fields)):
|
||
rec[fields[i]] = l[i]
|
||
self.onRecord(rec)
|
||
lno += 1
|
||
self.fd.close()
|
||
self.onFinish()
|
||
except Exception as e:
|
||
self.fd.close()
|
||
raise e
|
||
```
|
||
|
||
---
|
||
|
||
## 🧩 继承与扩展建议
|
||
|
||
可通过继承 `CSVData` 类来实现更复杂的功能:
|
||
|
||
```python
|
||
class MyCSVProcessor(CSVData):
|
||
def onBegin(self):
|
||
print("开始处理数据...")
|
||
|
||
def onRecord(self, rec):
|
||
# 示例:过滤价格大于 100 的记录
|
||
if float(rec['close_price']) > 100:
|
||
print("高价值记录:", rec)
|
||
|
||
def onFinish(self):
|
||
print("数据处理完毕!")
|
||
```
|
||
|
||
---
|
||
|
||
## 📎 总结
|
||
|
||
| 特性 | 支持情况 |
|
||
|------|----------|
|
||
| 自定义字段名 | ✅ |
|
||
| 跳过标题行 | ✅ |
|
||
| 指定数据起始行 | ✅ |
|
||
| 全量读取 | ✅ (`read`) |
|
||
| 流式读取 | ✅ (`iterRead`) |
|
||
| 可扩展性 | ✅(支持回调钩子) |
|
||
| 错误处理 | ⚠️ 存在缺陷,需修复 |
|
||
| 编码支持 | ❌ 缺失,建议增加 |
|
||
|
||
---
|
||
|
||
## 📌 版本建议(改进方向)
|
||
|
||
1. 增加 `encoding` 参数,默认 `'utf-8'`
|
||
2. 修复异常捕获与方法命名
|
||
3. 使用上下文管理器 (`with open(...)`) 替代手动关闭文件
|
||
4. 添加类型注解提升可读性
|
||
5. 支持分隔符参数(如 tab、分号等)
|
||
|
||
---
|
||
|
||
📘 **结论**:`CSVData` 是一个结构清晰、易于扩展的 CSV 处理类,稍作修正后可用于生产环境。 |