阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。
两行明细的金额相同,不能说明它们是重复数据;排序后的表格更整齐,也不能说明它仍与原文逐行对应。提取明细时,最容易丢掉的不是字段,而是行与来源之间的关系。
先保存稳定的行身份
为每条明细保留来源页、表格编号和源行号,或一个由这些信息构成的稳定标识。产品名和金额可能重复,不适合单独作为身份。下游需要按金额排序时,可以建立展示视图,但不要把排序后的序号误写回原始来源。
JSON 数组本身保留顺序,不过 schema 里的
uniqueItems
不是“某个字段唯一”的快捷写法。两个对象只要其他字段不同,就可能仍被视为不同项。字段级唯一性要单独检查。
一组同金额、不同来源的明细
from collections import Counter
rows = [{"source_id": "p1-t1-r1", "item": "Bolt", "amount": "10.00"},
{"source_id": "p1-t1-r2", "item": "Nut", "amount": "10.00"}]
def check_ids(items):
counts = Counter(row["source_id"] for row in items)
return [key for key, count in counts.items() if count > 1]
assert check_ids(rows) == []
assert len(rows) == 2
assert check_ids(rows + [dict(rows[0])]) == ["p1-t1-r1"]
display = sorted(rows, key=lambda row: row["item"], reverse=True)
assert [r["source_id"] for r in rows] == ["p1-t1-r1", "p1-t1-r2"]
assert {r["source_id"] for r in display} == {r["source_id"] for r in rows}
这是合成数据的身份检查,不是表格识别器。代码故意不把相同金额去重,也不把显示顺序当成来源顺序。真实来源 ID 的生成规则需要考虑文件版本,否则两个版本的同一页同一行可能被误当成同一证据。
行数不对时先找丢失环节
现象
应检查
不应立即采取
金额重复
是否是不同项目或不同源行
按金额去重
来源 ID 重复
是否重复导入或映射错误
随机补一个新 ID
行数减少
合并单元格、空行与过滤规则
用上一行补齐
顺序变化
是否仅为展示排序
覆盖原始行号
跨页表头不是普通数据行
重复表头、续表说明和小计行都可能进入明细数组。删除它们需要可解释的规则,不能把“第一行”或“文字较短”当成唯一条件。保留被排除的行及理由,可以帮助审校发现误删,而不是只能在最终总额对不上时追查。
把来源关系带到译后核对
多语言明细表应能按来源 ID 对齐,不能只按译后产品名查找。评估
OmniDoc(产品入口)
的表格文档翻译结果时,行列关系、数量和单位都值得单独核对。本文的来源标识是外层工程约定,不是对平台内部实现的说明。
参考:
JSON Schema 数组
。
- 1 -
更新于 2026-10-08