
本文介绍一种基于正则表达式的可靠方法,用于修复无空格编号列表(如“1.pizza2.burger”)在语音合成中的误读问题,自动在数字编号与文字间插入空格,并严格截取前3项,兼顾可读性与tts友好性。
在语音合成(TTS)场景中,连续紧凑的编号列表(例如 "1.Pizza2.Burger3.Sushi")极易被引擎误判为单个词(如将 "1.Pizza" 读作“一一点披萨”或连读成“十一点萨”),严重影响听感。根本原因在于编号与后续文本间缺乏语义分隔——缺少空格,且标点(如 .)常混杂多余空白(如 "1 . Pizza")。理想输出应为规范、易解析的格式:"1. Pizza 2. Burger 3. Sushi",即:每个编号后紧跟一个英文句点、一个空格,再接内容;且仅保留前三项。
直接对原始字符串按换行或冒号分割(如原代码中 split(":") + split("\n"))在实际文本中并不可靠——输入往往不含换行符,而是平铺在一行内,且编号之间无分隔。此时,正则表达式是更鲁棒的解决方案。
以下是一个经过验证的专业实现:
import re
def post_processing(text):
"""
针对语音合成优化列表格式:
- 提取前3个形如 '数字+任意空白+点+内容' 的编号项;
- 清理编号后的冗余空格(如 '1 .' → '1.');
- 在每个编号项末尾统一添加空格,实现项间分隔。
Args:
text (str): 原始输入文本(可能含冒号引导语、URL等)
Returns:
str: 格式化后的列表字符串,如 "1. Pizza 2. Burger 3. Sushi"
"""
# 步骤1:移除URL(避免干扰匹配)
text = re.sub(r"https?://\S+", "", text)
# 步骤2:使用高精度正则提取前3个编号项
# \b 确保数字开头不被部分匹配;\s* 容忍任意空白;\..*? 非贪婪匹配到下一个编号前
pattern = r"\b(\d+\s*\..*?)(\d+\s*\..*?)(\d+\s*\..*?)(?=\d+\.|$)"
match = re.search(pattern, text, re.DOTALL)
if not match:
return text.strip() # 未匹配到列表,返回清理后的原文
# 步骤3:对每个捕获组标准化编号格式(如 '1 .' → '1.'),并去除末尾多余标点/空格
cleaned_items = []
for group in match.groups():
# 统一编号格式:数字 + 紧跟句点(移除中间空格)
normalized = re.sub(r"(\d)\s*\.", r"\1.", group)
# 移除项内结尾可能的重复标点(如多个点、省略号)和尾部空白
trimmed = re.sub(r"[.]{2,}$", ".", normalized.strip())
cleaned_items.append(trimmed)
# 步骤4:用单个空格连接三项,形成TTS友好的线性列表
return " ".join(cleaned_items)
# ✅ 测试用例
text = "Suggestions for restaurants:1 . Pizza2. Burger3. Sushi4. Noodles..."
print(post_processing(text)) # 输出:1. Pizza 2. Burger 3. Sushi关键设计说明:
- ✅ 精准定位:\b(\d+\s*\..*?) 捕获每个“数字+任意空白+点+后续内容”的最小单元,(?=\d+\.|$) 确保第三项后紧跟第四项或字符串结束,避免过度匹配;
- ✅ 鲁棒清洗:re.sub(r"(\d)\s*\.", r"\1.", ...) 将 "1 ."、"2."、"3 ." 全部归一为 "1."、"2."、"3.";
- ✅ 防错兜底:若未匹配到三组(如原文仅两项),函数安全返回原文清理版,避免崩溃;
- ✅ TTS就绪:最终输出为纯空格分隔的短字符串,无换行、无多余标点,完美适配主流语音引擎的分词逻辑。
⚠️ 注意事项:该方案假设编号为阿拉伯数字(1.、2.…),不支持罗马数字或字母编号(如 a.、i.)。如需扩展,可将 \d+ 替换为更通用的编号模式(如 [0-9a-zA-Z]+),但需同步增强边界判断以防误匹配。此外,若原始文本中编号与内容间存在括号、破折号等复杂分隔符,建议在 .*? 后增加针对性清洗逻辑。










