当前位置:首页 > Python 工业工具 > 正文内容

文件与异常:让程序"摔倒了也能爬起来"

文件与异常:让程序"摔倒了也能爬起来"

一、问题背景:跑了5分钟的脚本,第300行崩了

上个月遇到一个经典状况。

写了一个批量处理脚本,分析50个Lot的数据。跑了5分钟,到第43个Lot时崩了:

```

ZeroDivisionError: division by zero

```

原因:第43个Lot的数据文件是空的,`len(data) == 0`,然后代码做了 `sum(data) / len(data)` —— 除0了。

更崩溃的是:前42个Lot的结果也丢了,因为程序一崩啥都没保存。

这个问题的本质是:你把所有鸡蛋放在一个篮子里,篮子一摔全碎了。

学完这一篇,你能做到:

1. 文件不存在/空文件/格式不对——程序都能"优雅处理",不崩

2. 用 `try-except` 让程序跳过有问题的数据继续跑

3. 哪怕其中一段崩了,前面的结果也保存得好好的

---

二、技术原理:try-except = 安全气囊

2.1 最常见的两种报错

```python

1. 文件不存在

open("不存在的文件.csv") # FileNotFoundError

2. 除数为0

1 / 0 # ZeroDivisionError

```

报错后程序直接停止。后面的所有代码都不执行了。

2.2 try-except 解决

```python

安全气囊版

try:

file = open("data.csv")

content = file.read()

file.close()

print("文件读取成功")

except FileNotFoundError:

print("文件不存在,跳过")

print("程序继续运行!") # 即使上面出错了,这里照样执行

```

为什么这样写? `try` 块里的代码就像开车,`except` 就是安全气囊——出事了气囊弹出来,人没事(程序继续跑)。

2.3 不用try-except的隐患

```python

=== 脆弱版 ===

data = open("data.csv").readlines() # 文件不存在?直接崩

avg = sum(float(x) for x in data) / len(data) # 空文件?也崩

=== 健壮版 ===

try:

with open("data.csv", 'r') as f:

data = f.readlines()

except FileNotFoundError:

print("data.csv 不存在,使用默认数据")

data = ["1250", "1248", "1251"]

try:

nums = [float(x.strip()) for x in data]

avg = sum(nums) / len(nums)

except ZeroDivisionError:

print("数据为空,平均值为0")

avg = 0

except ValueError:

print("数据格式不对,跳过")

avg = 0

print(f"平均值为: {avg}")

```

---

三、实战案例:安全的Lot数据分析

3.1 读取CSV文件

假设每天从MES导出一个CSV文件,格式如下:

```

lot_id,thickness_1,thickness_2,thickness_3,thickness_4,thickness_5

FAB001,1250.5,1248.3,1251.2,1249.8,1250.1

FAB002,1247.5,1250.8,1249.1,1251.5,1248.2

```

```python

import csv

def read_lot_csv(file_path):

"""

安全读取Lot数据CSV

参数:

file_path: CSV文件路径

返回:

list[dict]: 解析后的数据

"""

try:

with open(file_path, 'r', encoding='utf-8') as f:

reader = csv.DictReader(f)

data = list(reader)

if not data:

print("⚠ 文件为空")

return []

print(f"✅ 成功读取 {len(data)} 条记录")

return data

except FileNotFoundError:

print(f"⚠ 文件不存在: {file_path}")

return []

except UnicodeDecodeError:

# 有些MES导出的文件是GBK编码

try:

with open(file_path, 'r', encoding='gbk') as f:

return list(csv.DictReader(f))

except:

print("⚠ 编码问题,无法读取")

return []

except Exception as e:

print(f"⚠ 读取失败: {e}")

return []

使用

data = read_lot_csv("mes_data.csv")

if data:

print(f"第1批: {data[0]}")

```

为什么这样写? 函数返回空列表而不是抛异常——调用者可以安全地处理 "没数据" 的情况,比如 `if not data: use_default()`。另外要注意的是,中文Windows导出的CSV经常是GBK编码,所以 `UnicodeDecodeError` 时要尝试用 `gbk` 重新打开。

3.2 安全的Lot分析

```python

def safe_analyze_lots(csv_data):

"""

安全分析多批Lot数据

跳过有问题的数据,不影响其他Lot的分析

"""

results = []

success = 0

failed = 0

for i, row in enumerate(csv_data):

try:

# 提取厚度数据(5个测量值)

thickness = []

for key in ['thickness_1', 'thickness_2', 'thickness_3',

'thickness_4', 'thickness_5']:

val = float(row.get(key, 0))

thickness.append(val)

# 计算统计量

avg = sum(thickness) / len(thickness)

# Cpk计算

usl = 1253 # 规格上限

lsl = 1247 # 规格下限

std = (sum((x - avg)**2 for x in thickness) /

(len(thickness) - 1)) ** 0.5

cpk = min((usl - avg) / (3 std), (avg - lsl) / (3 std)) if std > 0 else 0

results.append({

'lot_id': row.get('lot_id', f'Unknown-{i}'),

'avg': round(avg, 2),

'std': round(std, 2),

'cpk': round(cpk, 3),

'pass': cpk >= 1.0

})

success += 1

except (ValueError, ZeroDivisionError, KeyError) as e:

print(f"⚠ 第{i+2}行数据异常: {e},已跳过")

failed += 1

continue # 跳过这一行,继续分析下一个

print(f"分析完成: 成功{success}个, 跳过{failed}个")

return results

使用

results = safe_analyze_lots(data)

for r in results[:3]:

status = "✅" if r['pass'] else "⚠"

print(f"{status} {r['lot_id']}: avg={r['avg']}, Cpk={r['cpk']}")

```

最关键的技巧:`try` 只包裹可能出错的部分(提取数值和计算),`except` 里用 `continue` 跳过有问题的 Lot。这样就算第10个Lot数据坏了,前9个和后40个都正常分析。

3.3 把结果保存回去

```python

def save_results(results, output_path="analysis_result.csv"):

"""

保存分析结果

如果写入失败,不崩溃,只报错

"""

try:

with open(output_path, 'w', encoding='utf-8', newline='') as f:

writer = csv.DictWriter(f, fieldnames=['lot_id', 'avg', 'std', 'cpk', 'pass'])

writer.writeheader()

writer.writerows(results)

print(f"✅ 结果已保存: {output_path}")

return True

except PermissionError:

print(f"⚠ 无权限写入: {output_path}")

return False

except Exception as e:

print(f"⚠ 保存失败: {e}")

return False

完整流程

data = read_lot_csv("mes_20260115.csv")

if data:

results = safe_analyze_lots(data)

save_results(results)

```

---

四、效果对比

| 情况 | 不加异常处理 | 加异常处理 |

|------|------------|-----------|

| 文件不存在 | 程序直接崩溃 | 提示"文件不存在",用默认数据 |

| 空文件 | 除0错误崩溃 | 返回空列表,继续往下走 |

| 某行数据格式错 | 第10行崩了,前面9行白干 | 跳过第10行,继续后面的 |

| 编码问题 | UnicodeDecodeError | 自动切换gbk编码 |

| 无写入权限 | PermissionError崩溃 | 打印错误,数据不丢 |

一句话总结:不加异常处理是"全部或全不"——要么全对,要么全崩。加异常处理是"尽力而为"——好的留下,坏的跳过。

---

五、自己动手

```python

练习:读取你自己的数据

import csv

假设你有一个数据文件 data.csv,内容是:

id,value

1,123

2,abc

3,456

练习1:安全读取文件,处理文件不存在的异常

练习2:计算value的平均值,跳过非数字的行

练习3:用try-except保护整个过程

✏️ 下面写你的代码

```

思考题

1. try-except 和 if-else 有什么区别?(提示:try处理"意料之外的错误",if处理"可以预见的情况")

2. 为什么不用一个大的try包住所有代码?试试看如果不知道具体哪行出错,调试是不是更困难?

3. `finally` 是用来做什么的?(提示:关闭文件、释放资源)

---

六、常见误区

| 误区 | 问题 | 正确做法 |

|------|------|---------|

| `except:` 不指定异常类型 | 捕获所有异常,包括Ctrl+C | 指定具体类型如 `except FileNotFoundError:` |

| try包一大坨代码 | 不知道哪行出的错 | 每个可能出错的步骤单独try |

| 捕获了异常但不处理 | `except: pass` 静默吞错误 | 至少打印日志 `print(f"错误: {e}")` |

| 只用 `else` 不用 `finally` | 资源可能没释放 | 用 `finally` 或 `with` 语句 |

---

> 💬 你写代码时有没有"跑了一个小时崩了"的经历?评论区聊聊

> 📚 收藏+点赞,下一篇开始搞数据分析 👆

相关文章

FAB工程师学Python的正确路径(附学习地图)

FAB工程师学Python的正确路径(附学习地图)

FAB工程师学Python的正确路径(附学习地图) 我带过一个实习生,非科班出身,学了3个月Python,第一个月工资就涨了2000。 也有干了5年的工艺工程师,手动导数据画图画了5年,月薪还是那点钱...

Python+半导体数据工具完整自学路线(零基础→项目实战)

Python+半导体数据工具完整自学路线(零基础→项目实战)

Python+半导体数据工具完整自学路线(零基础→项目实战) 经常有人问我:我想学Python做FAB数据分析,从哪里开始? 今天我把完整路线画出来,从零基础到能独立做项目,按这个走,90天能出师。...

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集 我在FAB干了15年,最值钱的东西不是经验,是一个攒了多年的Python工具箱。 今天把这个工具箱的核心部分分享出来,从数据采集到SP...

Python日报自动化:MES数据一键生成Excel报告(附完整源码)

Python日报自动化:MES数据一键生成Excel报告(附完整源码)

Python日报自动化:MES数据一键生成Excel报告(附完整源码) 1. 我的血泪史:每天2小时的日报工作 2018年,我在FAB做整合工程师的时候,每天早上第一件事不是分析数据,而是做日报。从M...

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动 1. 问题背景:被动维修的代价 FAB里最贵的不是设备,是设备宕机造成的产能损失。一台光刻机价值$100M+,停机1小时损失约$...

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码)

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码)

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码) 1. 问题背景:我的第一次良率分析 2016年,我在FAB做工艺工程师的时候,第一次被要求分析一批良率异常。工程师把数据发给我——一个E...