通过Edge-tts生成的中文字幕如何自然断句?
2025-08-01 15:17:15 20 分享链接 开发笔记 python
重新分配字幕的关键在于将words-in-cue参数设置为一个,然后将生成的临时字幕文件与配音文案匹配,根据文案的断句标识到字幕断句处进行合并。
subtitles.py
import srt
import re
from datetime import timedelta
def clean_text(text):
"""
统一的文本清洗规则(对原始字幕和输入文本同时生效):
1. 移除所有非中文字符、非英文字符、非数字的字符(如《》、标点、空格等)
2. 转为小写(忽略大小写差异)
确保原始字幕和输入文本经过相同处理后进行匹配
"""
# 保留中文字符(\u4e00-\u9fa5)、英文字母(a-zA-Z)、数字(0-9)
cleaned = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', '', text)
# 转为小写,消除大小写影响
return cleaned.lower()
def read_srt(file_path):
"""读取SRT文件,返回按顺序排列的字幕条目列表"""
with open(file_path, 'r', encoding='utf-8') as f:
srt_content = f.read()
subs = list(srt.parse(srt_content))
return sorted(subs, key=lambda x: x.index)
def read_txt_lines(file_path):
"""读取input.txt,返回按行分割的句子列表(过滤空行)"""
with open(file_path, 'r', encoding='utf-8') as f:
return [line.strip() for line in f.read().split('\n') if line.strip()]
def find_matching_subs(original_subs, target_line, start_idx=0):
"""
对称清洗匹配算法:
- 对input.txt的目标行进行清洗(使用clean_text)
- 对input.srt的每个条目内容也进行相同清洗(使用同一个clean_text)
- 比较双方清洗后的内容,确保规则完全一致
"""
# 对输入文本行进行清洗(使用统一规则)
target_clean = clean_text(target_line)
if not target_clean:
return (None, None) # 空内容不匹配
current_clean = "" # 累计清洗后的原始字幕内容(使用与目标行完全相同的规则)
# 从start_idx开始遍历原始字幕
for i in range(start_idx, len(original_subs)):
# 对原始字幕条目内容进行清洗(使用同一个clean_text函数,确保规则一致)
sub_clean = clean_text(original_subs[i].content.strip())
current_clean += sub_clean # 累加清洗后的内容
# 检查累计的清洗内容是否与目标清洗内容完全匹配
if current_clean == target_clean:
return (start_idx, i)
# 若累计内容已超过目标长度,提前终止(避免无效循环)
if len(current_clean) > len(target_clean):
return (None, None)
# 遍历结束仍未找到匹配
return (None, None)
def merge_subtitles(original_subs, txt_lines):
"""根据txt_lines合并original_subs中的条目,使用对称清洗匹配算法"""
merged_subs = []
current_sub_idx = 0 # 当前处理到的原始字幕索引
for line_idx, line in enumerate(txt_lines):
# 查找当前行对应的连续原始字幕条目
start_idx, end_idx = find_matching_subs(original_subs, line, current_sub_idx)
if start_idx is None or end_idx is None:
# 输出清洗后的内容用于调试(双方使用相同规则处理后的结果)
target_clean = clean_text(line)
# 输出从当前索引开始的原始字幕清洗后的内容片段,方便对比
sample_clean = ""
for i in range(current_sub_idx, min(current_sub_idx + 10, len(original_subs))):
sample_clean += clean_text(original_subs[i].content.strip())
print(f"警告:第{line_idx+1}行文本匹配失败")
print(f" 输入文本:{line}")
print(f" 输入文本清洗后:{target_clean}")
print(f" 原始字幕起始位置清洗后片段:{sample_clean}")
print(f" (从原始字幕第{current_sub_idx+1}条开始)\n")
continue
# 合并时间:取第一个条目的start和最后一个条目的end
merged_start = original_subs[start_idx].start
merged_end = original_subs[end_idx].end
# 创建合并后的字幕条目(保留原始输入格式)
merged_sub = srt.Subtitle(
index=line_idx + 1,
start=merged_start,
end=merged_end,
content=line
)
merged_subs.append(merged_sub)
# 更新处理索引
current_sub_idx = end_idx + 1
return merged_subs
def save_srt(subtitles, file_path):
"""保存合并后的SRT文件"""
with open(file_path, 'w', encoding='utf-8') as f:
f.write(srt.compose(subtitles))
def main(original_srt_path, txt_path, output_srt_path):
original_subs = read_srt(original_srt_path)
if not original_subs:
print("原始SRT文件为空或格式错误")
return
txt_lines = read_txt_lines(txt_path)
if not txt_lines:
print("input.txt为空或无有效内容")
return
merged_subs = merge_subtitles(original_subs, txt_lines)
if merged_subs:
save_srt(merged_subs, output_srt_path)
print(f"已生成合并后的字幕文件:{output_srt_path}")
print(f"成功合并 {len(merged_subs)} 条字幕(共处理 {len(txt_lines)} 行文本)")
else:
print("未生成任何合并后的字幕")
if __name__ == "__main__":
# 示例路径(根据实际情况修改)
main(
"D:/222/input.srt", # 原始SRT(每个词/短语一个条目)
"D:/222/input.txt", # 每行是需要合并的句子
"D:/222/output.srt" # 输出合并后的SRT
)
input.txt
继续来看《小明宗门》第583章
整个天羽宗山门广场一片死寂
无数天羽宗弟子双目圆睁
根本无法相信战局竟瞬间逆转
在他们心中
input.srt
1
00:00:00,100 --> 00:00:00,450
继续
2
00:00:00,450 --> 00:00:00,900
来看
3
00:00:00,925 --> 00:00:01,675
小明宗门
4
00:00:01,675 --> 00:00:01,937
第
5
00:00:02,037 --> 00:00:02,912
583
6
00:00:02,912 --> 00:00:03,175
章
7
00:00:03,737 --> 00:00:04,125
整个
8
00:00:04,150 --> 00:00:04,687
天羽宗
9
00:00:04,712 --> 00:00:05,050
山门
10
00:00:05,062 --> 00:00:05,575
广场
11
00:00:05,675 --> 00:00:06,050
一片
12
00:00:06,062 --> 00:00:06,587
死寂
13
00:00:07,162 --> 00:00:07,562
无数
14
00:00:07,587 --> 00:00:08,125
天羽宗
15
00:00:08,137 --> 00:00:08,637
弟子
16
00:00:08,725 --> 00:00:09,075
双目
17
00:00:09,075 --> 00:00:09,300
圆
18
00:00:09,300 --> 00:00:09,575
睁
19
00:00:10,137 --> 00:00:10,512
根本
20
00:00:10,525 --> 00:00:10,862
无法
21
00:00:10,862 --> 00:00:11,387
相信
22
00:00:11,487 --> 00:00:12,025
战局
23
00:00:12,050 --> 00:00:12,225
竟
24
00:00:12,237 --> 00:00:12,650
瞬间
25
00:00:12,662 --> 00:00:13,137
逆转
26
00:00:13,712 --> 00:00:13,887
在
27
00:00:13,900 --> 00:00:14,162
他们
28
00:00:14,162 --> 00:00:14,712
心中
output.srt
1
00:00:00,100 --> 00:00:03,175
继续来看《小明宗门》第583章
2
00:00:03,737 --> 00:00:06,587
整个天羽宗山门广场一片死寂
3
00:00:07,162 --> 00:00:09,575
无数天羽宗弟子双目圆睁
4
00:00:10,137 --> 00:00:13,137
根本无法相信战局竟瞬间逆转
5
00:00:13,712 --> 00:00:14,712
在他们心中
核心优化点说明
新增文本清洗机制:
通过clean_text
函数移除所有特殊字符(如《》、()、标点符号等)和空格,只保留中文字符、英文字母和数字,并转为小写。例如:- 原始字幕内容:
小明宗门
→ 清洗后:小明宗门
- input.txt内容:
继续来看《小明宗门》第583章
→ 清洗后:继续来看小明宗门第583章
清洗后双方核心文字完全匹配,解决了特殊字符导致的匹配失败问题。
- 原始字幕内容:
匹配逻辑优化:
- 只比较清洗后的核心文字,忽略原始格式差异
- 当累计的原始字幕清洗内容长度超过目标行时,提前终止循环(提高效率)
- 增加详细的调试信息,方便定位匹配失败的原因(如打印清洗后的文本)
- 保留原始格式:
虽然匹配时使用清洗后的文本,但生成的字幕文件会保留input.txt
中的原始格式(含特殊字符),兼顾匹配准确性和输出美观性。
针对你的示例场景的效果
- 原始SRT第3行:
小明宗门
(清洗后:小明宗门
) - input.txt第一行:
继续来看《小明宗门》第583章
(清洗后:继续来看小明宗门第583章
) - 算法会正确匹配原始SRT的1-6行(
继续
+来看
+小明宗门
+第
+583
+章
),合并后的时间为第1行的开始到第6行的结束,完美解决特殊字符导致的匹配失败问题。
最近更新
- 2025-08-04 16:13
- 详细介绍一下 tkinter 的pack布局参数
- 2025-08-03 17:50
- pyinstaller --onefile --windowed 与 pyinstaller -F -w的区别
- 2025-08-03 17:39
- 使用 PyInstaller 打包 Python 程序时 隐藏调用其它程序的命令窗口。
- 2025-08-03 11:04
- 使用 PyInstaller 打包 Python 程序时 -F 与 -D的区别。
- 2025-08-01 15:15
- 通过Edge-tts生成的中文字幕如何自然断句?
- 2025-07-31 18:23
- Edge-tts库 命令行工具有哪些常用参数?
- 2025-07-29 01:43
- 豆包连环画生成提示(优化版)
- 2025-07-28 13:20
- 镜头运动手法:不止推拉,这些技巧让画面更有张力。
- 2025-07-28 13:13
- 摄影与剪辑是视频创作的两个核心环节,二者共同决定了作品的最终呈现效果。
- 2025-07-23 16:35
- 在Python中如何获取脚本所在的目录?