"""Постобработка распознанного текста. Две задачи: чинить систематические ошибки модели на английских терминах (GigaAM обучена только на русском и коверкает их предсказуемым образом) и приводить типографику к принятому виду. """ import re from pathlib import Path __all__ = ["apply_replacements", "load_replacements", "normalize_typography"] # Все виды тире, которые встречаются в выводе моделей, включая минус. _DASHES = "‐‑‒–—―−" _DASH_RE = re.compile(f"\\s*[{_DASHES}]\\s*") # Кавычка-ёлочка, приклеенная к предыдущему слову без пробела. _GLUED_QUOTE_RE = re.compile(r"(?<=[^\s])(«)") _MULTISPACE_RE = re.compile(r"[ \t]{2,}") def load_replacements(path: Path) -> dict[str, str]: """Читает словарь замен из файла вида `что искать = на что заменить`. Пустые строки и строки, начинающиеся с #, игнорируются, как и строки без разделителя - чтобы опечатка в файле не роняла сервис. """ if not Path(path).is_file(): return {} rules: dict[str, str] = {} for line in Path(path).read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue src, dst = line.split("=", 1) src, dst = src.strip(), dst.strip() if src: rules[src.lower()] = dst return rules def apply_replacements(text: str, rules: dict[str, str]) -> str: """Заменяет термины по словарю, не трогая совпадения внутри слов. Длинные правила применяются первыми, иначе «так менеджер» съел бы часть «гугл так менеджер» и более точное правило уже не сработало бы. """ if not rules: return text for src in sorted(rules, key=len, reverse=True): pattern = re.compile(rf"(? str: """Приводит типографику к принятому виду: тире в дефис, отбивка кавычек.""" text = _DASH_RE.sub(" - ", text) text = _GLUED_QUOTE_RE.sub(r" \1", text) text = _MULTISPACE_RE.sub(" ", text) return text.strip()