117 lines
5.0 KiB
Python
117 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
OCR乱码检测器 — Step1 OCR完成后必跑。
|
|
扫描OCR文本中所有疑似乱码行,输出vision核实清单。
|
|
每一处乱码都必须vision对应PDF页面核实后才能进Step2。
|
|
|
|
用法: python3 ocr-garble-detect.py <ocr_file.md>
|
|
|
|
判定规则:
|
|
1. 多个无意义英文片段(连续3+字母非常见词)
|
|
2. 中文占比过低(正常合同行>50%,低于30%标红)
|
|
3. 中英混杂碎片(典型OCR乱码模式)
|
|
4. 异常符号密集(>20%非正常字符)
|
|
|
|
误报处理:
|
|
- 纯数字表格行(日期+金额)→ 正常,核对数字即可
|
|
- 邮箱/网址/银行账号 → 正常
|
|
- 英文缩写(USD/RMB/PDF)→ 正常
|
|
真正需要vision的是:含中文碎片+英文乱码的混合行(如"oe方,同Se")
|
|
|
|
教训(凤凰文化0701):
|
|
Line 204 "Se【2024】年【10】月【15】日" 被跳过未核实,
|
|
导致整段违约金条款丢失(实际是"初年年租金20%作为违约金")。
|
|
代价=审查结论反转("无违约金"→"有20%违约金")。
|
|
"""
|
|
import re, sys
|
|
|
|
def is_garbled(line):
|
|
"""判断一行是否疑似乱码,返回原因或None"""
|
|
stripped = line.strip()
|
|
if not stripped or len(stripped) < 5:
|
|
return None
|
|
|
|
# 1. 连续3+个无意义英文字母组合(非常见英文词)
|
|
nonsense_en = re.findall(r'[a-zA-Z]{3,}', stripped)
|
|
common_words = {'pdf','ocr','usd','rmb','the','and','for','with','from',
|
|
'www','com','xdf','jpg','png','doc','docx','xlsx','occ',
|
|
'vision','step','check','null','true','false'}
|
|
real_nonsense = [w for w in nonsense_en
|
|
if w.lower() not in common_words
|
|
and not re.match(r'^[A-Z]{1,4}$', w)]
|
|
if len(real_nonsense) >= 2:
|
|
return f"多个无意义英文片段: {real_nonsense[:3]}"
|
|
|
|
# 2. 单行中文字符占比过低(正常合同行中文应>50%)
|
|
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', stripped))
|
|
total_chars = len(re.findall(r'\S', stripped))
|
|
if total_chars > 10 and chinese_chars / total_chars < 0.3:
|
|
# 排除纯数字表格行(日期+金额)和邮箱/账号行
|
|
if re.match(r'^[\d\.\-\s\|/,]+$', stripped):
|
|
return None # 纯数字表格行
|
|
if '@' in stripped or re.match(r'^[A-Z]{2,4}:', stripped):
|
|
return None # 邮箱或字段标签
|
|
return f"中文占比过低({chinese_chars}/{total_chars}={chinese_chars/total_chars:.0%})"
|
|
|
|
# 3. 常见OCR乱码模式:小写英文碎片+中文混杂
|
|
if re.search(r'[a-z]{2,}\s+[a-z]{2,}\s+[a-z]{2,}', stripped) and chinese_chars > 0:
|
|
return "中英混杂碎片(典型OCR乱码)"
|
|
|
|
# 3b. 日期区域英文字母污染(凤凰文化10.2教训:Se【2024】年【10】月→整段丢失)
|
|
if re.search(r'[A-Za-z]{2,}\s*【\d{4}】', stripped) and chinese_chars > 0:
|
|
return "日期区域字母污染(高风险:可能整段条款被OCR吃掉)"
|
|
|
|
# 4. 特殊符号异常密集
|
|
special = len(re.findall(r'[^\w\u4e00-\u9fff\s,。、;:""''()【】《》\-\+\.\%\/\|]', stripped))
|
|
if special > 5 and total_chars > 0 and special / total_chars > 0.2:
|
|
return f"异常符号密集({special}个)"
|
|
|
|
return None
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("用法: python3 ocr-garble-detect.py <ocr_file.md>")
|
|
sys.exit(1)
|
|
|
|
filepath = sys.argv[1]
|
|
with open(filepath, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
garbled = []
|
|
for i, line in enumerate(lines, 1):
|
|
reason = is_garbled(line)
|
|
if reason:
|
|
garbled.append((i, line.strip()[:80], reason))
|
|
|
|
if not garbled:
|
|
print("✅ 未检测到明显乱码行。可以进入Step 2。")
|
|
else:
|
|
# 区分真乱码 vs 可能误报(纯数字/邮箱等)
|
|
real_garble = [g for g in garbled if '无意义英文' in g[2] or '中英混杂' in g[2]]
|
|
maybe_garble = [g for g in garbled if g not in real_garble]
|
|
|
|
print(f"⚠️ 检测到 {len(garbled)} 处疑似乱码(其中 {len(real_garble)} 处高危)\n")
|
|
|
|
if real_garble:
|
|
print("🔴 高危乱码(必须vision核实,不核实不进Step2):")
|
|
for lineno, text, reason in real_garble:
|
|
print(f" Line {lineno:3d} | {reason}")
|
|
print(f" | {text}")
|
|
print()
|
|
|
|
if maybe_garble:
|
|
print("🟡 疑似乱码(核对数字/格式是否正确):")
|
|
for lineno, text, reason in maybe_garble:
|
|
print(f" Line {lineno:3d} | {reason}")
|
|
print(f" | {text}")
|
|
print()
|
|
|
|
print(f"--- 高危 {len(real_garble)} 处必须vision核实 + 疑似 {len(maybe_garble)} 处核对数值 ---")
|
|
print("操作:对每处高危乱码,定位PDF页码 → vision精读 → 记录修正内容")
|
|
print("全部消灭后才能进入 Step 2。")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|