🎬字幕处理:USF 字幕格式转换为 SRT

在影音处理、字幕整理、视频本地化等场景中,我们经常会遇到不同字幕格式需要互转的情况,其中 USF(Universal Subtitle Format) 是一种基于 XML 的字幕格式,而 SRT(SubRip Subtitle)则是最常见、最被播放器和编辑软件支持的格式。

本文分享一段 实战 Python 代码,完成 USF → SRT 转换,并对处理流程、注意事项、关键逻辑逐行解析,开箱即用。


⏳ USF 与 SRT 有什么区别?

特性 USF SRT
结构 基于 XML 文本文件
时间格式 HH:MM:SS.sss HH:MM:SS,mmm
支持样式 字体、大小、颜色等丰富信息 纯文本(基础格式)
主流播放器支持 较少 广泛支持

因此,当我们从专业字幕制作工具导出 USF 后,转 SRT 才能在视频播放器、剪辑软件、硬字幕工具中正常使用。


🧠 本文解决的核心问题

✔ 解析 USF 字幕
✔ 将时间转换为 SRT 格式
✔ 处理跨天字幕(24:00→00:00 的情况)
✔ 处理多 text 节点合并
✔ 确保字幕序号、文本、时长合法
✔ 一行代码调用完成转换


🧩 完整 Python 代码(USF → SRT)

import re
from pathlib import Path
import xml.etree.ElementTree as ET


def parse_hms_to_seconds(time_str: str) -> float:
    m = re.match(r"^(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$", time_str.strip())
    if not m:
        raise ValueError(f"Invalid time format: {time_str}")
    h, mi, s, ms = m.groups()
    hours = int(h)
    minutes = int(mi)
    seconds = int(s)
    millis = int(ms) if ms is not None else 0
    return hours * 3600 + minutes * 60 + seconds + millis / 1000.0


def format_seconds_to_srt(total_seconds: float) -> str:
    total_ms = int(round(total_seconds * 1000))
    hours = total_ms // 3_600_000
    rem = total_ms % 3_600_000
    minutes = rem // 60_000
    rem = rem % 60_000
    seconds = rem // 1000
    millis = rem % 1000
    return f"{hours:02d}:{minutes:02d}:{seconds:02d},{millis:03d}"


def clean_text(text: str) -> str:
    t = text.replace("\r\n", "\n").replace("\r", "\n")
    t = re.sub(r"\n{3,}", "\n\n", t)
    return t.strip()


def convert_usf_to_srt(usf_path: Path, srt_path: Path | None = None) -> Path:
    if srt_path is None:
        srt_path = usf_path.with_suffix(".srt")

    tree = ET.parse(usf_path)
    root = tree.getroot()

    subs_root = root.find(".//subtitles")
    if subs_root is None:
        raise ValueError("USF file missing <subtitles> section")

    items = []
    day_offset = 0
    prev_base_start = None

    for sub in subs_root.findall("subtitle"):
        start_attr = sub.get("start")
        stop_attr = sub.get("stop")
        if not start_attr or not stop_attr:
            continue

        base_start = parse_hms_to_seconds(start_attr)
        base_stop = parse_hms_to_seconds(stop_attr)

        if prev_base_start is not None and base_start < prev_base_start:
            day_offset += 1

        start_seconds = base_start + day_offset * 86400
        stop_seconds = base_stop + day_offset * 86400
        if base_stop < base_start:
            stop_seconds += 86400

        texts = []
        for tnode in sub.findall("text"):
            content = "".join(tnode.itertext())
            if content:
                texts.append(clean_text(content))
        text_joined = "\n".join([t for t in texts if t])
        if not text_joined:
            text_joined = ""

        items.append((start_seconds, stop_seconds, text_joined))
        prev_base_start = base_start

    lines = []
    for idx, (st, en, txt) in enumerate(items, start=1):
        start_str = format_seconds_to_srt(st)
        end_str = format_seconds_to_srt(en)
        lines.append(str(idx))
        lines.append(f"{start_str} --> {end_str}")
        lines.append(txt)
        lines.append("")

    srt_path.write_text("\n".join(lines), encoding="utf-8")
    return srt_path


def main():
    usf_file = Path(r"20251120.usf")
    out_file = Path(r"20251120.srt")
    srt_path = convert_usf_to_srt(usf_file, out_file)
    print(f"SRT written: {srt_path}")


if __name__ == "__main__":
    main()

🔎 核心逻辑解析

① USF 时间解析(带毫秒)

USF 中时间格式为:

HH:MM:SS.sss

使用正则解析并转换为秒:

parse_hms_to_seconds()

例如:

00:03:12.500 → 192.5 秒

② 防止跨天错误

有些字幕可能这样:

start stop
1 23:59:55 23:59:58
2 00:00:02 00:00:06

看起来像倒退了,其实是跨天。

代码自动维护 day_offset,避免:

❌ 00:00:02 比 23:59:55 更早


③ 合并多段文本内容

USF 一个 subtitle 下可能有多个 <text>,例如:

<subtitle start="00:03:00" stop="00:03:05">
    <text>第一行</text>
    <text>第二行</text>
</subtitle>

输出自动:

第一行
第二行

④ SRT 输出格式

符合标准:

序号
HH:MM:SS,mmm --> HH:MM:SS,mmm
字幕内容

📦 如何使用?

1️⃣ 将 .usf 放到脚本所在目录
2️⃣ 修改:

usf_file = Path("example.usf")

3️⃣ 运行:

python convert.py

生成:

example.srt

播放器、硬字幕工具立即可用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐