程序难免出错。这一章教你:出错时主动报错,以及兜住错误不让程序崩溃。
发现自己处理不了的情况,用 抛出 喊停,带上一条说明:
函数 安全除法(甲, 乙)
如果 乙 == 0
抛出 "除数不能是零"
返回 甲 / 乙
抛出 后面跟一段说明(文字或任何值),这段说明会原样出现在捕获它的 错 里。抛出 的别名是 报错。抛出后程序立刻停下,除非有人「捕获」——没接住的话会报 CN0311。
不想让一个错误搞崩整个程序,就把它包在 尝试 里,用 捕获 接住:
下面是接在上面
安全除法函数之后运行的片段;单独复制时要把函数定义一起带上。
尝试
设 商 = 安全除法(10, 0)
打印(商)
捕获 错
打印("算不了:" + 错)
尝试 里的代码正常跑捕获,错 就是错误说明捕获 里的内容就跳过捕获 的别名是 出错时。
也可以这么写:
抛出/捕获/最后各有别名(报错/出错时/无论如何),效果一样。这是 CNplus 的「一种意思多种写法」特性,详见关键字与别名。
不关心错误到底是什么时,捕获 后面可以不写变量:
尝试
整数("abc")
捕获
打印("那不是数字")
有些收尾的事(关文件、清理)无论成败都要做,放 最后:
尝试
打印("开始")
抛出 "出错了"
捕获 错
打印("捕获:" + 错)
最后
打印("清理完成(无论成败都执行)")
最后 段保证执行——哪怕 尝试 里 返回 了、或错误继续往上抛,最后 都会先跑完。适合放关文件、清理这类收尾。
最后 的别名是 无论如何。
「文字转数字」可能失败,用异常包住就能给个默认值:
函数 安全转数(文字, 默认 = 0)
尝试
返回 整数(文字)
捕获 错
返回 默认
打印(安全转数("42")) # 42
打印(安全转数("abc")) # 0(转不了,给默认)
完整的例子见 示例/16-安全计算器.cnp。
捕获 能接住运行中出的错——除以零、类型不对、下标越界、转换失败、库调用出错、以及你自己 抛出 的。但语法错误不行:那是程序还没开始跑就发现的,抓不住。
学过 Python 的人,看这里——这一章学的,用 Python 写是这样。零基础可以直接跳过,去下面做「练一练」。
先把这一章串成一个完整程序(CNplus):
尝试
整数("abc")
捕获 错
打印("接住了:" + 错)
同一个程序,用 Python 写:
try: # 尝试
int("abc")
except Exception as 错: # 捕获 错
print("接住了:" + str(错))
| CNplus | Python |
|---|---|
抛出 "说明" |
raise ValueError("说明") |
尝试 |
try: |
捕获 错 |
except Exception as 错: |
最后 |
finally: |
捕获(不写变量) |
except:(裸 except) |
一个差异:CNplus 的 捕获 直接接住所有运行时错误;Python 的 except 能按异常类型分别捕获(except ValueError、except TypeError),更精细。CNplus 简化了——新手先会「兜住」就够,分类捕获是进阶话题。
先把下面程序保存为 /tmp/条件字面量.cnp:
如果 0
打印("条件为真,开始执行")
cnp 检查 /tmp/条件字面量.cnp
cnp 运行 /tmp/条件字面量.cnp
这个错误在程序运行前就能发现,两条命令都会报告 CN0301。
再把下面程序保存为 /tmp/条件变量.cnp:
设 条件 = 0
如果 条件
打印("条件为真,开始执行")
cnp 检查 /tmp/条件变量.cnp
cnp 运行 /tmp/条件变量.cnp
这一次第一条会通过,第二条会在运行时报告 CN0301。把第一行改成 设 条件 = 真 后再执行同样两条命令:检查和运行都会通过,运行时打印“条件为真,开始执行”。完整诊断含义见诊断码。
下一步:做练习,然后继续下一课。