Perl 12:解析失败不能变成导入了前几条
接收三条日志时,如果第二条是 DEBUG 而等级集合只允许 INFO/WARN/ERROR,打印“已导入一条”会制造部分成功的错觉。旧四字段入口将每个文件作为一次读入事务:按行计数和验证,任一条不满足字段语法就报错;没有结果对象返回给 CLI,也不写出已解析的前缀。
当前语法是四个 UTF-8 字段。时间形状为固定 YYYY-MM-DDTHH:MM:SSZ;等级取三个大写词;服务名仅允许 ASCII 字母开头、后续字母/数字/下划线/连字符;消息可以空,却不能带 TAB、CR 或 LF。read_records 在读取时先查字段数,再查取值范围,最后才构造四键哈希引用。fixtures/invalid.tsv 的 DEBUG 被拒绝:import STDOUT 空、STDERR line 1: invalid record、退出码 2;同样不能把多余制表符静默吞进消息字段。
为什么把报错中的行号当接口?原始合成文件可以由两条有效记录加上一条错误记录拼出;行号从一次 read_records 调用内部的 my $line_number 计起,不能在同一个进程处理第二个文件时继续累计。这样调用者至少知道修哪一行,却不能误认为错误已细到字符列;这一阶段没有列号,也没有可恢复的逐行错误列表。客户端读到状态 2 必须把整次导入判作失败。
可恢复坏行必须有独立结果接口
新版五字段入口选择另一种明确的契约:语法错误进入 rejected 数组,合法行进入 accepted 数组,文件字节和 I/O 错误仍终止整批。import_records 返回一个哈希引用,包含 input 计数和两份数组;调用方不会依赖“标量上下文返回最后字段”的隐式行为。每次调用使用自己的局部行号,拒绝对象含 line、error,以及可解码坏行的 raw 文本。
这不是在旧版抛错后随意继续,而是先重新定义错误分类,再增加接口和金标。解析函数的异常在行边界立即保存,随后才构造拒绝对象。合法行之后遇到坏日期,输入总数仍增加,拒绝数增加;遇到非法 UTF-8,函数直接失败,没有已返回的完整批次。调用者必须同时查看程序状态和产物,不能从上次接受文件的存在推导此次成功。
t/course-core.t 固定五条输入、四条接受、一条拒绝以及第五行的行号。有效数与拒绝数之和等于输入数,是比“最后打印四”更强的验收:某一行既被重复计数又被丢弃时,具体接受内容和拒绝金标还须逐项比较,守恒式本身也不是全部证明。
[PATTERN] 错误可恢复需要数据模型支持。先定义哪些错误能定位到一条记录,再把拒绝作为结果的一部分;不能靠捕获所有异常来获得可靠容错。
练习:把第二条记录的等级改成 WARN,再执行旧入口导入,之前的“1 条已成功”应怎样解释?不能恢复一个未发布的前缀结果;应该重新验证完整输入,得到最终成功总数。新版则从完整重放得到新接受与拒绝列表,两套行为分别比较。测试命令和金标见 examples/perl-language/evidence/11-14.md 与补全证据,具体输入见 fixtures/demo.tsv、fixtures/invalid.tsv、fixtures/course.tsv。
| 错误类别 | 新版处理 |
|---|---|
| 字段或日期错误 | 记录拒绝对象 |
| 超长行 | 拒绝并恢复下一行 |
| 非法 UTF-8 | 整批失败 |
| I/O 错误 | 整批失败 |
参考官方 Perl 5.38.2 pod/perlfunc.pod(split、die)、pod/perldata.pod(List value constructors)。






