用 PYTHON 编写的一个分镜段落文本与SRT字幕合并匹配的函数实例
2025-07-06 11:37:59 4 分享链接 开发笔记 python
def on_format_materials():
text = text_box.get("1.0", tk.END).strip()
if not text:
messagebox.showerror("错误", "文本框内容不能为空,请输入有效的文本。")
return
if not subtitle_content:
messagebox.showerror("错误", "请先选择字幕文件!")
return
# 解析字幕内容为结构化数据(合并多行文本)
subtitle_blocks = parse_subtitles(subtitle_content)
if not subtitle_blocks:
messagebox.showerror("错误", "字幕格式不正确,无法解析!")
return
# 获取整个字幕的最后结束时间
overall_end_time = subtitle_blocks[-1]['end_time'] if subtitle_blocks else ""
# 尝试按空行分隔段落
paragraphs = text.split('\n\n')
if len(paragraphs) == 1:
# 若没有空行分隔,按序号分隔段落
pattern = re.compile(r'^\d+\.\s', re.MULTILINE)
matches = pattern.finditer(text)
indices = [match.start() for match in matches]
paragraphs = []
for i in range(len(indices)):
if i == len(indices) - 1:
paragraphs.append(text[indices[i]:].strip())
else:
paragraphs.append(text[indices[i]:indices[i + 1]].strip())
# 清理段落:移除序号和空白
cleaned_paragraphs = []
for para in paragraphs:
# 移除行首数字序号(如 "1. ", "2) " 等)
para = re.sub(r'^\s*\d+[.\)]\s*', '', para, flags=re.MULTILINE)
cleaned_paragraphs.append(para.strip())
# 为每个段落设置初始结束时间(整个字幕的最后结束时间)
paragraph_end_times = {i: overall_end_time for i in range(len(cleaned_paragraphs))}
# 从后往前处理每个段落
last_matched_time = overall_end_time # 记录上一个匹配的结束时间
for p_idx in range(len(cleaned_paragraphs) - 1, -1, -1):
para = cleaned_paragraphs[p_idx]
para_lines = para.split('\n')
matched = False # 标记当前段落是否匹配成功
# 从后往前处理段落中的每一行
for line_idx in range(len(para_lines) - 1, -1, -1):
line = para_lines[line_idx].strip()
if not line:
continue
best_match = None
best_match_index = -1
# 从后往前匹配字幕块
for s_idx in range(len(subtitle_blocks) - 1, -1, -1):
sub_block = subtitle_blocks[s_idx]
sub_text = sub_block['text']
# 计算相似度(基于前缀子串,结果为1.0或0.0)
similarity = calculate_similarity(line, sub_text)
# 只要匹配成功(相似度为1.0),立即记录并跳出循环
if similarity == 1.0:
best_match = sub_block
best_match_index = s_idx
break # 找到匹配后立即退出字幕循环
# 如果找到匹配,更新段落结束时间并删除已匹配索引及之后的所有字幕
if best_match is not None:
paragraph_end_times[p_idx] = best_match['end_time']
last_matched_time = best_match['end_time'] # 更新最后匹配时间
matched = True # 标记为已匹配
# 删除从best_match_index到结尾的所有字幕块
if best_match_index != -1:
del subtitle_blocks[best_match_index:]
# 跳出段落行循环,继续处理下一个段落
break
# 如果当前段落未匹配到任何字幕,使用上一个匹配的结束时间
if not matched:
paragraph_end_times[p_idx] = last_matched_time
# 构建最终格式化文本
formatted_text = ""
for i, para in enumerate(cleaned_paragraphs):
if para:
formatted_text += para + '\n' + paragraph_end_times[i] + '\n\n'
formatted_text = formatted_text.strip()
text_box.delete("1.0", tk.END)
text_box.insert(tk.END, formatted_text)
def parse_subtitles(subtitle_content):
"""解析字幕内容为结构化数据,将多行文本合并为一行"""
blocks = []
pattern = re.compile(r'(\d+)\n(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\n([\s\S]*?)(?=\n\n|$)')
for match in pattern.finditer(subtitle_content):
index = match.group(1)
start_time = match.group(2)
end_time = match.group(3)
# 将多行文本合并为一行,用空格连接
text = ' '.join(match.group(4).strip().split('\n'))
blocks.append({
'index': index,
'start_time': start_time,
'end_time': end_time,
'text': text
})
return blocks
def calculate_similarity(line1, line2):
"""根据前缀子串关系计算相似度(结果为1.0或0.0)"""
# 预处理:转换为小写并移除标点
line1_clean = re.sub(r'[^\w\s]', '', line1).lower()
line2_clean = re.sub(r'[^\w\s]', '', line2).lower()
# 完全相等检查
if line1_clean == line2_clean:
return 1.0
# 获取长度关系
len1, len2 = len(line1_clean), len(line2_clean)
# 段落文本长度 > 字幕文本
if len1 > len2:
# 检查字幕是否是段落的前缀子串
return 1.0 if line1_clean.startswith(line2_clean) else 0.0
# 段落文本长度 < 字幕文本
elif len1 < len2:
# 检查段落是否是字幕的前缀子串
return 1.0 if line2_clean.startswith(line1_clean) else 0.0
# 长度相等但内容不同
else:
return 0.0
以下是整合了所有优化建议的完整代码:
def on_format_materials():
text = text_box.get("1.0", tk.END).strip()
if not text:
messagebox.showerror("错误", "文本框内容不能为空,请输入有效的文本。")
return
if not subtitle_content:
messagebox.showerror("错误", "请先选择字幕文件!")
return
# 解析字幕内容为结构化数据(合并多行文本)
subtitle_blocks = parse_subtitles(subtitle_content)
if not subtitle_blocks:
messagebox.showerror("错误", "字幕格式不正确,无法解析!")
return
# 获取整个字幕的最后结束时间
overall_end_time = subtitle_blocks[-1]['end_time'] if subtitle_blocks else ""
# 尝试按空行分隔段落
paragraphs = text.split('\n\n')
if len(paragraphs) == 1:
# 若没有空行分隔,按序号分隔段落
pattern = re.compile(r'^\d+\.\s', re.MULTILINE)
matches = pattern.finditer(text)
indices = [match.start() for match in matches]
paragraphs = []
for i in range(len(indices)):
if i == len(indices) - 1:
paragraphs.append(text[indices[i]:].strip())
else:
paragraphs.append(text[indices[i]:indices[i + 1]].strip())
# 清理段落:移除序号和空白
cleaned_paragraphs = []
for para in paragraphs:
# 移除行首数字序号(如 "1. ", "2) " 等)
para = re.sub(r'^\s*\d+[.\)]\s*', '', para, flags=re.MULTILINE)
cleaned_paragraphs.append(para.strip())
# 为每个段落设置初始结束时间(整个字幕的最后结束时间)
paragraph_end_times = {i: overall_end_time for i in range(len(cleaned_paragraphs))}
# 从后往前处理每个段落
for p_idx in range(len(cleaned_paragraphs) - 1, -1, -1):
para = cleaned_paragraphs[p_idx]
para_lines = para.split('\n')
# 从后往前处理段落中的每一行
for line_idx in range(len(para_lines) - 1, -1, -1):
line = para_lines[line_idx].strip()
if not line:
continue
best_match = None
best_match_index = -1
# 从后往前匹配字幕块
for s_idx in range(len(subtitle_blocks) - 1, -1, -1):
sub_block = subtitle_blocks[s_idx]
sub_text = sub_block['text']
# 计算相似度(基于前缀子串,结果为1.0或0.0)
similarity = calculate_similarity(line, sub_text)
# 只要匹配成功(相似度为1.0),立即记录并跳出循环
if similarity == 1.0:
best_match = sub_block
best_match_index = s_idx
break # 找到匹配后立即退出字幕循环
# 如果找到匹配,更新段落结束时间并删除已匹配索引及之后的所有字幕
if best_match is not None:
paragraph_end_times[p_idx] = best_match['end_time']
# 删除从best_match_index到结尾的所有字幕块
if best_match_index != -1:
del subtitle_blocks[best_match_index:] # 关键优化:删除当前及之后的所有字幕
# 跳出段落行循环,继续处理下一个段落
break
# 构建最终格式化文本
formatted_text = ""
for i, para in enumerate(cleaned_paragraphs):
if para:
formatted_text += para + '\n' + paragraph_end_times[i] + '\n\n'
formatted_text = formatted_text.strip()
text_box.delete("1.0", tk.END)
text_box.insert(tk.END, formatted_text)
def parse_subtitles(subtitle_content):
"""解析字幕内容为结构化数据,将多行文本合并为一行"""
blocks = []
pattern = re.compile(r'(\d+)\n(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\n([\s\S]*?)(?=\n\n|$)')
for match in pattern.finditer(subtitle_content):
index = match.group(1)
start_time = match.group(2)
end_time = match.group(3)
# 将多行文本合并为一行,用空格连接
text = ' '.join(match.group(4).strip().split('\n'))
blocks.append({
'index': index,
'start_time': start_time,
'end_time': end_time,
'text': text
})
return blocks
def calculate_similarity(line1, line2):
"""根据前缀子串关系计算相似度(结果为1.0或0.0)"""
# 预处理:转换为小写并移除标点
line1_clean = re.sub(r'[^\w\s]', '', line1).lower()
line2_clean = re.sub(r'[^\w\s]', '', line2).lower()
# 完全相等检查
if line1_clean == line2_clean:
return 1.0
# 获取长度关系
len1, len2 = len(line1_clean), len(line2_clean)
# 段落文本长度 > 字幕文本
if len1 > len2:
# 检查字幕是否是段落的前缀子串
return 1.0 if line1_clean.startswith(line2_clean) else 0.0
# 段落文本长度 < 字幕文本
elif len1 < len2:
# 检查段落是否是字幕的前缀子串
return 1.0 if line2_clean.startswith(line1_clean) else 0.0
# 长度相等但内容不同
else:
return 0.0
核心优化点:
删除已匹配索引后的所有字幕:
del subtitle_blocks[best_match_index:]
- 确保后续段落只能匹配到更早的字幕
- 大幅减少循环次数,提高性能
严格的前缀子串匹配:
- 只判断是否为前缀子串(返回1.0或0.0)
- 移除了所有相似度阈值判断
提前终止循环:
- 找到第一个匹配后立即退出循环
- 避免不必要的后续比较
这个版本在保持匹配准确性的同时,通过批量删除字幕块显著优化了性能,特别适合处理较长的字幕文件和文本内容。
最近更新
- 2025-07-06 11:37
- 用 PYTHON 编写的一个分镜段落文本与SRT字幕合并匹配的函数实例
- 2025-07-04 18:20
- 详细列出 drawtext 滤镜在 ffmpeg7.1.1 支持的参数列表
- 2025-07-04 16:33
- windows cmd 中调用 FFmpeg 命令的注意事项
- 2025-07-04 16:18
- Windows 命令行解析复杂参数时的典型错误问题
- 2025-07-03 22:50
- 详细介绍 FFmpeg7.1.1 subtitles 滤镜 force_style 支持的参数
- 2025-07-03 05:11
- 借助 FFmpeg 的 filter_complex 滤镜混合音频流的实例。
- 2025-07-03 02:26
- 借助 FFmpeg 的 drawtext 滤镜给视频添加标题。
- 2025-07-02 18:19
- 使用ffmpeg处理srt字幕的对齐和位置相关参数说明
- 2025-07-01 23:20
- 可以批量剪辑视频的软件有哪些呢?这五款超好用!
- 2025-05-10 17:40
- 零基础也能秒懂的场景设计课,从「叙事场景 / 氛围场景」分类到「分镜表模板」。