121 lines
5.1 KiB
Python
121 lines
5.1 KiB
Python
import re
|
||
|
||
|
||
# Единицы измерения со слэшем — раскрываем до чтения слэша
|
||
UNIT_SLASH = [
|
||
(r'\bкм\s*/\s*ч\b', 'километров в час'),
|
||
(r'\bм\s*/\s*с\b', 'метров в секунду'),
|
||
(r'\bкм\s*/\s*с\b', 'километров в секунду'),
|
||
(r'\bмб\s*/\s*с\b', 'мегабит в секунду'),
|
||
(r'\bгб\s*/\s*с\b', 'гигабит в секунду'),
|
||
(r'\bруб\s*/\s*мес\b', 'рублей в месяц'),
|
||
(r'\bр\s*/\s*мес\b', 'рублей в месяц'),
|
||
]
|
||
|
||
|
||
def clean_for_speech(text: str) -> str:
|
||
text = re.sub(r'\*+', '', text) # убрать **жирный**
|
||
text = re.sub(r'#+\s', '', text) # убрать ## заголовки
|
||
text = re.sub(r'- ', '', text) # убрать тире списков
|
||
text = re.sub(r'\[.*?\]\(.*?\)', '', text) # убрать ссылки
|
||
text = re.sub(r'\n+', '. ', text) # переносы → точки
|
||
|
||
# составные единицы со слэшем — до общей замены `/`
|
||
for pat, repl in UNIT_SLASH:
|
||
text = re.sub(pat, repl, text, flags=re.IGNORECASE)
|
||
|
||
# знаки перед числом: "+9", "-3", "±2"
|
||
text = re.sub(r'(^|\s)\+(\d)', r'\1плюс \2', text)
|
||
text = re.sub(r'(^|\s)-(\d)', r'\1минус \2', text)
|
||
text = re.sub(r'±(\d)', r'плюс-минус \1', text)
|
||
|
||
# дроби и отношения "12/15" → "12 из 15", "5/10" → "5 из 10"
|
||
text = re.sub(r'(\d+)\s*/\s*(\d+)', r'\1 из \2', text)
|
||
# одиночный слэш — как союз "или"
|
||
text = text.replace('/', ' или ')
|
||
|
||
# градусы и прочие символы
|
||
text = re.sub(r'°\s*C\b', ' градусов', text, flags=re.IGNORECASE)
|
||
text = re.sub(r'°\s*F\b', ' градусов Фаренгейта', text, flags=re.IGNORECASE)
|
||
text = text.replace('°', ' градусов')
|
||
text = text.replace('%', ' процентов')
|
||
text = text.replace('№', ' номер ')
|
||
text = text.replace('&', ' и ')
|
||
text = text.replace('@', ' собака ')
|
||
text = text.replace('×', ' на ')
|
||
|
||
# распространённые сокращения в полную форму — иначе TTS буквоедит
|
||
abbr = [
|
||
(r'\bт\.\s*е\.', 'то есть'),
|
||
(r'\bт\.\s*к\.', 'так как'),
|
||
(r'\bт\.\s*д\.', 'так далее'),
|
||
(r'\bт\.\s*п\.', 'тому подобное'),
|
||
(r'\bи\s*т\.\s*д\.', 'и так далее'),
|
||
(r'\bи\s*т\.\s*п\.', 'и тому подобное'),
|
||
(r'\bпо-\s*русски\b', 'по-русски'),
|
||
]
|
||
for pat, repl in abbr:
|
||
text = re.sub(pat, repl, text, flags=re.IGNORECASE)
|
||
|
||
# схлопываем дубли пунктуации
|
||
text = re.sub(r'([:;,!?])\s*\.', r'\1', text)
|
||
text = re.sub(r'\.\s*\.+', '.', text)
|
||
text = re.sub(r'\s+', ' ', text) # лишние пробелы
|
||
text = re.sub(r'(\d+)\.(\s)', r'\1\2', text)
|
||
return text.strip()
|
||
|
||
|
||
def find_sentence_end(text: str, min_len: int = 60) -> int:
|
||
"""Ищет конец предложения, игнорируя ложные точки"""
|
||
if len(text) < min_len:
|
||
return -1
|
||
|
||
for match in re.finditer(r'[.!?]', text):
|
||
pos = match.start()
|
||
if pos < min_len:
|
||
continue
|
||
|
||
before_1 = text[max(0, pos-1):pos] # 1 символ до
|
||
before_3 = text[max(0, pos-3):pos] # 3 символа до
|
||
after_2 = text[pos+1:pos+3] # 2 символа после
|
||
after_stripped = after_2.lstrip()
|
||
|
||
# 1. Цифра.Цифра → "0.76", "3.14"
|
||
if before_1.isdigit() and after_2[:1].isdigit():
|
||
continue
|
||
|
||
# 2. Цифра. Цифра → "1. 2 ГБ"
|
||
if before_1.isdigit() and after_stripped[:1].isdigit():
|
||
continue
|
||
|
||
# 3. Аббревиатуры → "ГБ.", "МБ.", "км.", "шт.", "руб.", "млн.", "млрд."
|
||
abbrevs = ["гб", "мб", "кб", "тб", "км", "см", "мм", "шт",
|
||
"руб", "млн", "млрд", "тыс", "кг", "гр", "мл",
|
||
"gb", "mb", "kb", "tb", "km", "ms", "kb"]
|
||
if any(before_3.lower().endswith(a) for a in abbrevs):
|
||
continue
|
||
|
||
# 4. Одиночная заглавная буква → "А.", "В.", "США." (инициалы/аббр.)
|
||
if len(before_3.strip()) == 1 and before_3.strip().isupper():
|
||
continue
|
||
|
||
# 5. После точки строчная буква → "load avg. нормально"
|
||
if after_stripped and after_stripped[0].islower():
|
||
continue
|
||
|
||
# 6. Многоточие → "..."
|
||
if text[pos:pos+3] == "...":
|
||
continue
|
||
|
||
# 7. Точка внутри URL или IP → "192.168.1.1", "example.com"
|
||
if before_1.isdigit() or (after_2[:1].isdigit() and "." in before_3):
|
||
continue
|
||
|
||
# 8. Процент с точкой → "95.5%"
|
||
if "%" in after_2[:2]:
|
||
continue
|
||
|
||
return pos
|
||
|
||
return -1
|