diff --git a/tests/realdata_contract.py b/tests/realdata_contract.py index 2284679..325539a 100644 --- a/tests/realdata_contract.py +++ b/tests/realdata_contract.py @@ -109,23 +109,50 @@ def prompt_rule_candidates() -> list[Path]: # SRT 解析(纯函数,供参考与产物共同使用) # --------------------------------------------------------------------------- -_SRT_BLOCK_RE = re.compile( - r"(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})\s*\n(.*?)(?=\n\s*\d+\s*\n|\Z)", - re.DOTALL, +# 匹配 SRT 时间轴行(时间戳 --> 时间戳),作为条目边界。 +_SRT_TIME_LINE_RE = re.compile( + r"(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})", ) def parse_srt_entries(text: str) -> list[dict]: - """解析 SRT 为 [{start, end, text}](秒为单位)。""" + """解析 SRT 为 [{start, end, text}](秒为单位)。 + + 按行分块:一个条目 = 序号行 + 时间轴行 + 若干文本行(可空)。即使文本为 + 空串(如翻译补空占位、空字幕)也计入一条,时间轴不丢失。""" entries: list[dict] = [] - for match in _SRT_BLOCK_RE.finditer(text): + lines = text.splitlines() + index = 0 + while index < len(lines): + line = lines[index].strip() + # 跳过序号行与空行,找时间轴行。 + if not line or not _SRT_TIME_LINE_RE.search(line): + index += 1 + continue + match = _SRT_TIME_LINE_RE.search(line) + start = _ts_to_seconds(match.group(1)) + end = _ts_to_seconds(match.group(2)) + index += 1 + # 收集后续非序号、非时间轴的文本行(可空/多行),直到空行或序号行。 + text_parts: list[str] = [] + while index < len(lines): + nxt = lines[index].strip() + if not nxt: + break # 空行:条目结束 + if _SRT_TIME_LINE_RE.search(nxt): + break # 下一个时间轴:条目结束 + if nxt.isdigit(): + break # 下一个序号:条目结束 + text_parts.append(nxt) + index += 1 entries.append( { - "start": _ts_to_seconds(match.group(1)), - "end": _ts_to_seconds(match.group(2)), - "text": match.group(3).strip().replace("\n", " "), + "start": start, + "end": end, + "text": " ".join(text_parts), } ) + index += 1 return entries