1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# Скрипт предварительной обработки текста для 5# синтезатора речи RHVoice Ольги Яковлевой 6# By Capricorn2001 & vantu5z 7 8from re import sub, finditer 9 10from .templates import (samples_1, samples_2, samples_3, samples_4, 11 units, zh_units, 12 forms, 13 pre_acc, 14 i_mu, i_sr, i_zh, i_mn, 15 r_ca, r_mn, r_mu, r_sr, r_zh, 16 d_ca, d_mn, d_mu, d_sr, d_zh, 17 v_ca, v_zh, 18 t_ca, t_mn, t_mu, t_sr, t_zh, 19 p_ca, p_mn, p_mu, p_sr, p_zh, 20 adj_pad, mn_pad, mu_pad, sr_pad, zh_pad, 21 greekletters, letternames) 22from .functions import (condition, cardinal, ordinal, roman2arabic, 23 substant, feminin, daynight, fraction) 24from .words_forms import Words, M_GENDER, Z_GENDER, S_GENDER 25 26# Для определения атрибутов слов 27words = Words() 28 29 30def text_prepare(text): 31 """ 32 ================================= 33 Основная функция обработки текста 34 ================================= 35 """ 36 37 # предварительная обработка текста 38 for sample in samples_1: 39 text = sub(sample[0], sample[1], text) 40 41 # ================= 42 # Единицы измерения 43 # ================= 44 45 # Винительный падеж 46 47 mask = (r'\b(' 48 r'(состав[авеийлотшщьюя]{2,6}|превы[сш][авеийлотшщьюя]{2,5}) (бы |)' 49 r')' 50 r'((\d+,|)(\d+) - |)(\d+,|)(\d+)_' + units) 51 for m in finditer(mask, text): 52 new = m.group(1) 53 if m.group(4): 54 if m.group(5): 55 new += decimal(m.group(5)[:-1], m.group(6), 5) 56 else: 57 if m.group(9) in zh_units: 58 new += feminin(m.group(6), 5) 59 else: 60 new += m.group(6) 61 new += ' - ' 62 if m.group(7): 63 new += decimal(m.group(7)[:-1], m.group(8), 5) + ' ' 64 new += forms[m.group(9)][2] 65 else: 66 if m.group(9) in zh_units: 67 new += feminin(m.group(8), 5) 68 else: 69 new += m.group(8) 70 new += ' ' + substant(m.group(8), m.group(9), 5) 71 text = text.replace(m.group(), new, 1) 72 73 # например: "диаметром в 2 см -> диаметром в 2 сантиметра" 74 mask = (r'\b([А-Яа-яё]{3,})' 75 r'( (ориентировочно |примерно |приблизительно |)в )' 76 r'((\d+,|)(\d+) - |)(\d+,|)(\d+)_' + units) 77 for m in finditer(mask, text): 78 if m.group(1).lower() in pre_acc: 79 new = m.group(1) + m.group(2) 80 if m.group(4): 81 if m.group(5): 82 new += decimal(m.group(5)[:-1], m.group(6), 5) 83 else: 84 if m.group(9) in zh_units: 85 new += feminin(m.group(6), 5) 86 else: 87 new += m.group(6) 88 new += ' - ' 89 if m.group(7): 90 new += decimal(m.group(7)[:-1], m.group(8), 5) + ' ' 91 new += forms[m.group(9)][2] 92 else: 93 if m.group(9) in zh_units: 94 new += feminin(m.group(8), 5) 95 else: 96 new += m.group(8) 97 new += ' ' + substant(m.group(8), m.group(9), 5) 98 text = text.replace(m.group(), new, 1) 99 100 # Родительный падеж 101 # пример: "С 5 см до -> С пяти сантиметров до" 102 mask = (r'\b([Сс] (почти |примерно |приблизительно |плюс |минус |))' 103 r'(\d+,|)(\d+)_' + units + ' до ') 104 for m in finditer(mask, text): 105 new = m.group(1) 106 if m.group(3): 107 new += fraction(m.group(3)[:-1], m.group(4), 1) 108 new += ' ' + forms[m.group(5)][2] 109 else: 110 new += m.group(4) 111 new += ' ' + substant(m.group(4), m.group(5), 1) 112 text = text.replace(m.group(), new + ' до ', 1) 113 114 # пример: "от 1 до 4 км -> от одного до четырёх километров" 115 for m in finditer(r'\b([Оо]т |[Сс]о? )(\d+,|)(\d+)( до (\d+,|)\d+_)' + units, text): 116 if m.group(2): 117 number = fraction(m.group(2)[:-1], m.group(3), 1) 118 else: 119 number = cardinal(m.group(3), r_ca) 120 if condition(m.group(3)) and m.group(6) in zh_units: 121 number = number[:-2] + 'й' 122 new = m.group(1) + number + m.group(4) + m.group(6) 123 text = text.replace(m.group(), new, 1) 124 125 mask = (r'\b(' 126 r'[Бб]олее|[Мм]енее|[Бб]ольше|[Мм]еньше|[Вв]ыше|[Нн]иже|[Дд]альше|' 127 r'[Оо]коло|[Сс]выше|[Дд]ля|[Дд]о|[Ии]з|[Оо]т|[Вв]место|' 128 r'[Вв] размере|[Бб]лиже|[Вв] течение|[Нн]ач[инаетсялоь]{2,7} с|' 129 r'[Вв]ладел[аеимухцыь]{2,5}|[Дд]остиг[авеийлнотшщюуья]{,5}|' 130 r'[Пп]ротив|[Пп]орядка|[Пп]осле' 131 r')' 132 r'( плюс | минус | )((\d+,|)(\d+)( - | или | и )' 133 r'(плюс |минус |)|)(\d+,|)(\d+)_' + units) 134 for m in finditer(mask, text): 135 if m.group(3): 136 if m.group(4): 137 prenum = fraction(m.group(4)[:-1], m.group(5), 1) 138 else: 139 prenum = cardinal(m.group(5), r_ca) 140 if condition(m.group(5)) and m.group(10) in zh_units: 141 prenum = prenum[:-2] + 'й' 142 prenum += m.group(6) + m.group(7) 143 else: 144 prenum = '' 145 if m.group(8): 146 number = fraction(m.group(8)[:-1], m.group(9), 1) + ' ' + forms[m.group(10)][2] 147 else: 148 number = cardinal(m.group(9), r_ca) 149 if condition(m.group(9)) and m.group(10) in zh_units: 150 number = number[:-2] + 'й' 151 number += ' ' + substant(m.group(9), m.group(10), 1) 152 new = m.group(1) + m.group(2) + prenum + number 153 text = text.replace(m.group(), new, 1) 154 155 # Дательный падеж 156 mask = (r'\b(' 157 r'([Кк]|рав[нагеийлмоcуюыхья]{2,6})' 158 r'( всего | почти | примерно | приблизительно | плюс | минус | )' 159 r')' 160 r'(\d+,|)(\d+)_' + units) 161 for m in finditer(mask, text): 162 if m.group(4): 163 number = fraction(m.group(4)[:-1], m.group(5), 2) + ' ' + forms[m.group(6)][2] 164 else: 165 number = m.group(5) + ' ' + substant(m.group(5), m.group(6), 2) 166 text = text.replace(m.group(), m.group(1) + number, 1) 167 # С предлогом "по" при указании количества 168 for m in finditer(r'\b([Пп]о (\d*1(000){0,3}))_' + units, text): 169 new = m.group(1) + ' ' + substant(m.group(2), m.group(4), 2) 170 text = text.replace(m.group(), new, 1) 171 172 # Творительный падеж 173 mask = (r'\b(([Мм]ежду|[Пп]о сравнению с|[Вв]ладе[авеийлмтюшщья]{1,7}) ' 174 r'(почти |приблизительно |примерно |плюс |минус |))' 175 r'((\d+,|)(\d+)' 176 r'( [-и] (почти |приблизительно |примерно |плюс |минус |))|)' 177 r'(\d+,|)(\d+)_' + units) 178 for m in finditer(mask, text): 179 new = m.group(1) 180 a = m.group(4) and not m.group(5) 181 if a and condition(m.group(6)) and m.group(11) in zh_units: 182 new += cardinal(m.group(6), t_ca)[:-2] + 'ой' + m.group(7) 183 else: 184 new += m.group(4) 185 if m.group(9): 186 new += fraction(m.group(9)[:-1], m.group(10), 3) + ' ' 187 new += forms[m.group(11)][2] 188 else: 189 new += m.group(10) + ' ' + substant(m.group(10), m.group(11), 3) 190 text = text.replace(m.group(), new, 1) 191 192 # Предложный падеж 193 mask = (r'\b([Вв]|[Оо]б?|[Пп]ри)' 194 r'(( плюс | минус | )(\d+,|)(\d+)( [-и] | или )| )' 195 r'(почти |примерно |приблизительно |плюс |минус |)' 196 r'(\d+,|)(\d+)_' + units) 197 for m in finditer(mask, text): 198 if m.group(2) == ' ': 199 pre = ' ' 200 else: 201 if m.group(4): 202 pre = fraction(m.group(4)[:-1], m.group(5), 4) + ' ' + forms[m.group(10)][2] 203 else: 204 pre = m.group(5) 205 pre = m.group(3) + pre + m.group(6) 206 number = m.group(7) 207 if m.group(8): 208 number += fraction(m.group(8)[:-1], m.group(9), 4) + ' ' + forms[m.group(10)][2] 209 else: 210 number += m.group(9) + ' ' + substant(m.group(9), m.group(10), 4) 211 text = text.replace(m.group(), m.group(1) + pre + number, 1) 212 213 # Предлог "по" при указании количества 214 for m in finditer(r'\b([Пп]о )(\d*[02-9]1|1)_' + units, text): 215 new = m.group(1) + m.group(2) + ' ' + substant(m.group(2), m.group(3), 2) 216 text = text.replace(m.group(), new, 1) 217 218 # Именительный 219 for m in finditer(r'\b(\d+,\d+)_' + units, text): 220 new = m.group(1) + ' ' + forms[m.group(2)][2] 221 text = text.replace(m.group(), new, 1) 222 for m in finditer(r'\b(\d+)_' + units, text): 223 new = m.group(1) + ' ' + substant(m.group(1), m.group(2)) 224 text = text.replace(m.group(), new, 1) 225 226 mask = (r'(' 227 r'тысяч[аимх]{,3}|' 228 r'(миллион|миллиард|триллион)(|ами|а[мх]?|ов)' 229 r')_' + units) 230 for m in finditer(mask, text): 231 new = m.group(1) + ' ' + forms[m.group(4)][1] 232 text = text.replace(m.group(), new, 1) 233 234 # Время в формате (h)h ч (m)m мин 235 for m in finditer(r'\b(\d{1,2}) ?ч ?(\d{1,2}) ?мин\b', text): 236 if condition(m.group(1)): 237 hours = ' час ' 238 elif m.group(1) in ('2', '3', '4', '02', '03', '04', '22', '23', '24'): 239 hours = ' часа ' 240 else: 241 hours = ' часов ' 242 if condition(m.group(2)): 243 minutes = ' минута' 244 elif m.group(2) in ('2', '3', '4', '02', '03', '04', '22', '23', '24', '32', '33', '34', '42', '43', '44', '52', '53', '54'): 245 minutes = ' минуты' 246 else: 247 minutes = ' минут' 248 new = m.group(1) + hours + feminin(m.group(2)) + minutes 249 text = text.replace(m.group(), new, 1) 250 251 # Время в формате (ч)ч:мм/(ч)ч.мм 252 253 for m in finditer(r'\b(([Вв]|[Нн]а) \d{1,2})[:.](\d\d)\b', text): 254 minutes = ' ' + feminin(m.group(3), 5) 255 text = text.replace(m.group(), m.group(1) + minutes, 1) 256 257 for m in finditer(r'\b([Кк] )(\d{1,2}):(\d\d)\b', text): 258 hours = cardinal(m.group(2), d_ca) 259 minutes = cardinal(m.group(3), d_ca) 260 if m.group(3) == '00': 261 minutes = '00' 262 else: 263 if m.group(3)[0] == '0': 264 minutes = '0_' + minutes 265 minutes = feminin(minutes, 2) 266 new = m.group(1) + hours + ' ' + minutes 267 text = text.replace(m.group(), new, 1) 268 269 mask = (r'\b([Дд]о |[Пп]осле |[Оо]коло |[Сс] )' 270 r'(\d{1,2})[:.](\d\d)\b') 271 for m in finditer(mask, text): 272 hours = cardinal(m.group(2), r_ca) 273 minutes = cardinal(m.group(3), r_ca) 274 if m.group(3) == '00': 275 minutes = '00' 276 else: 277 if m.group(3)[0] == '0': 278 minutes = '0_' + minutes 279 minutes = feminin(minutes, 1) 280 new = m.group(1) + hours + ' ' + minutes 281 text = text.replace(m.group(), new, 1) 282 283 # ======================= 284 # Порядковые числительные 285 # ======================= 286 287 # Чтение римских цифр в датах 288 289 mask = (r'\b(([IVX]+)( (-|или|и|по)( в (конце |начале |середине |)| ))|)' 290 r'([IVX]+)( в?в\.)') 291 for m in finditer(mask, text): 292 if m.group(1): 293 pre = roman2arabic(m.group(2)) + m.group(3) 294 else: pre = '' 295 new = pre + roman2arabic(m.group(7)) + m.group(8) 296 text = text.replace(m.group(), new, 1) 297 298 mask = (r'\b([IVX]+)( [-и] )([IVX]+)' 299 r'( век(ами?|ах?|ов)| (тысяче|сто)лети(ями?|ях?|й))\b') 300 for m in finditer(mask, text): 301 ending = m.group(4)[-1] 302 if ending == 'а': 303 num1 = ordinal(roman2arabic(m.group(1)), i_mu) 304 num2 = ordinal(roman2arabic(m.group(3)), i_mu) 305 elif ending == 'я': 306 num1 = ordinal(roman2arabic(m.group(1)), i_sr) 307 num2 = ordinal(roman2arabic(m.group(3)), i_sr) 308 elif ending == 'в' or ending == 'й': 309 num1 = ordinal(roman2arabic(m.group(1)), r_mu) 310 num2 = ordinal(roman2arabic(m.group(3)), r_mu) 311 elif ending == 'м': 312 num1 = ordinal(roman2arabic(m.group(1)), d_mu) 313 num2 = ordinal(roman2arabic(m.group(3)), d_mu) 314 elif ending == 'и': 315 num1 = ordinal(roman2arabic(m.group(1)), t_mu) 316 num2 = ordinal(roman2arabic(m.group(3)), t_mu) 317 else: 318 num1 = ordinal(roman2arabic(m.group(1)), p_mu) 319 num2 = ordinal(roman2arabic(m.group(3)), p_mu) 320 text = text.replace(m.group(), num1 + m.group(2) + num2 + m.group(4), 1) 321 322 for m in finditer(r'\b([Сс]о? )([IVX]+) по ', text): 323 new = m.group(1) + roman2arabic(m.group(2)) + '-го по ' 324 text = text.replace(m.group(), new, 1) 325 326 # применение шаблонов 327 for sample in samples_2: 328 text = sub(sample[0], sample[1], text) 329 330 # например: "во 2 окне -> во втором окне" 331 mask = (r'\b([Вв]о?|[Нн]а|[Оо]б?|[Пп]ри) ' 332 r'(\d*[02-9]|\d*1\d) ([а-яё]+)\b') 333 for m in finditer(mask, text): 334 attr = words.get_attr(m.group(3)) 335 number = '' 336 if attr.have([S_GENDER, M_GENDER], False, [5]): 337 number = ordinal(m.group(2), p_mu) 338 elif attr.have([Z_GENDER], False, [2, 5]): 339 number = ordinal(m.group(2), p_zh) 340 if number: 341 new = m.group(1) + ' ' + number + ' ' + m.group(3) 342 text = text.replace(m.group(), new, 1) 343 344 # например: "со 2 примером -> со вторым примером" 345 mask = (r'\b([Сс]о? )(\d*1\d|\d*[02-9]?[02-9]) ([а-яё]+)\b') 346 for m in finditer(mask, text): 347 number = '' 348 attr = words.get_attr(m.group(3)) 349 if attr.have([M_GENDER, S_GENDER], False, [4]): 350 number = ordinal(m.group(2), t_mu) 351 elif attr.have([Z_GENDER], False, [2, 4, 5]): 352 number = ordinal(m.group(2), t_zh) 353 if number: 354 new = m.group(1) + number + ' ' + m.group(3) 355 text = text.replace(m.group(), new, 1) 356 357 # например: "на 8-м этаже -> на восьмом этаже" 358 for m in finditer(r'(\d+)-(м|й) ([а-яё]+)\b', text): 359 number = '' 360 attr = words.get_attr(m.group(3)) 361 if m.group(2) == 'м': 362 if attr.have([M_GENDER, S_GENDER], None, [4]): 363 number = ordinal(m.group(1), t_mu) 364 elif attr.have([M_GENDER, S_GENDER], None, [5]): 365 number = ordinal(m.group(1), p_mu) 366 elif m.group(2) == 'й': 367 if attr.have([Z_GENDER], False, [2, 4, 5]): 368 number = ordinal(m.group(1), t_zh) 369 if number: 370 text = text.replace(m.group(), number + ' ' + m.group(3), 1) 371 372 for m in finditer(r'(\d+)-е (([а-яё]+[ео]е ){,2}([а-яё]+[ео]))\b', text): 373 if words.have(m.group(4), [S_GENDER], False, [0, 3]): 374 new = ordinal(m.group(1), i_sr) + ' ' + m.group(2) 375 text = text.replace(m.group(), new, 1) 376 377 for m in finditer(r'\b(\d*11|\d*[05-9]) ([а-яё]+)\b', text): 378 attr = words.get_attr(m.group(2)) 379 if attr.have([M_GENDER], False, [3]) and not attr.have(case=[0]): 380 new = ordinal(m.group(1), r_mu) + ' ' + m.group(2) 381 text = text.replace(m.group(), new, 1) 382 383 for m in finditer(r'\b(\d*11|\d*[02-9]) ([а-яё]+)\b', text): 384 if words.have(m.group(2), [Z_GENDER], False, [3]): 385 new = ordinal(m.group(1), r_mu)[:-3] + 'ую ' + m.group(2) 386 text = text.replace(m.group(), new, 1) 387 388# for m in finditer(r'(\d+)-ю ([а-яё]+)\b', text): 389# if words.have(m.group(2), [Z_GENDER], False, [3]): 390# new = ordinal(m.group(1), v_zh) + ' ' + m.group(2) 391# text = text.replace(m.group(), new) 392 393 mask = (r'\b(?<!.)(?<!,)(?<!:)(\d*[02-9][05-9]|\d*1\d|[5-9]) ([а-яё]+)\b') 394 for m in finditer(mask, text): 395 number = '' 396 attr = words.get_attr(m.group(2)) 397 if attr.have([M_GENDER, S_GENDER], False, [1]): 398 number = ordinal(m.group(1), r_mu) 399 if attr.have([Z_GENDER], False, [1]): 400 number = ordinal(m.group(1), r_zh) 401 if number: 402 new = number + ' ' + m.group(2) 403 text = text.replace(m.group(), new, 1) 404 405 for sample in samples_3: 406 for m in finditer(sample[0], text): 407 text = text.replace(m.group(), eval(sample[1]), 1) 408 409 # Прилагательные, в состав которых входят числительные (3-кратный и т.п.) 410 for m in finditer(r'\b((\d+) - |)(\d+)-([а-яё]{5,})\b', text): 411 if m.group(1) == '': pre = '' 412 else: 413 if m.group(2)[-3:] == '000': 414 pre = cardinal(m.group(2)[:-3], r_ca) + 'тысяче - ' 415 else: pre = cardinal(m.group(2), r_ca) + ' - ' 416 if m.group(3)[-3:] == '000': 417 num = cardinal(m.group(3)[:-3], r_ca) + 'тысяче' 418 else: num = cardinal(m.group(3), r_ca) 419 num = pre + num 420 num = sub('ста', 'сто', num) 421 num = sub(r'(одной тысячи|одноготысяче)', 'тысяче', num) 422 num = sub(r'\bодного', 'одно', num) 423 text = text.replace(m.group(), num + '-' + m.group(4), 1) 424 425 # Количественные числительные 426 427 # Родительный падеж 428 mask = (r'\b([Оо]т|[Сс])' 429 r'( почти | примерно | приблизительно | плюс | минус | )' 430 r'((\d+,|)(\d+)( [-и] | или )|)(\d+,|)(\d+)' 431 r'(' 432 r' до( почти | примерно | приблизительно | плюс | минус | )' 433 r'((\d+,|)\d+( [-и] | или )|)(\d+,|)\d+' 434 r'( ([а-яё]+([иы]х|[ео]й|[ео]го) |и более |и менее |)([а-яё]+)|)' 435 r')\b') 436 for m in finditer(mask, text): 437 if m.group(3): 438 if m.group(4): 439 pre = fraction(m.group(4)[:-1], m.group(5), 1) 440 else: 441 pre = cardinal(m.group(5), r_ca) 442 if pre[-6:] == 'одного' and m.group(18) is not None: 443 if words.have(m.group(18), [Z_GENDER], None, [1]): 444 pre = pre[:-2] + 'й' 445 elif m.group(18) == 'суток': 446 pre = pre[:-3] + 'их' 447 pre += m.group(6) 448 else: 449 pre = '' 450 if m.group(7): 451 number = fraction(m.group(7)[:-1], m.group(8), 1) 452 else: 453 number = cardinal(m.group(8), r_ca) 454 if number[-6:] == 'одного' and m.group(18) is not None: 455 if words.have(m.group(18), [Z_GENDER], None, [1]): 456 number = number[:-2] + 'й' 457 elif m.group(18) == 'суток': 458 number = number[:-3] + 'их' 459 new = m.group(1) + m.group(2) + pre + number + m.group(9) 460 text = text.replace(m.group(), new, 1) 461 462 # Родительный падеж второго числительного в конструкции 463 # "числительное + существительное + вместо/из/против + числительное" 464 mask = (r'\b((\d+ )([а-яё]{3,})( вместо | из | против ))(\d+,|)(\d+)\b') 465 for m in finditer(mask, text): 466 attr = words.get_attr(m.group(3)) 467 if m.group(5): 468 number = decimal(m.group(5)[:-1], m.group(6), 1) 469 else: 470 number = cardinal(m.group(6), r_ca) 471 if condition(m.group(6)) and attr.have([Z_GENDER]): 472 number = number[:-2] + 'й' 473 elif number[-6:] == 'одного' and m.group(3) in ('сутки', 'суток', 'суткам', 'сутками','сутках'): 474 number = number[:-3] + 'их' 475 new = m.group(1) + number 476 text = text.replace(m.group(), new, 1) 477 478 mask = (r'\b(' 479 r'[Бб]олее|[Мм]енее|[Бб]ольше|[Мм]еньше|[Вв]ыше|[Нн]иже|[Дд]альше|' 480 r'[Дд]ороже|[Дд]ешевле|[Оо]коло|[Сс]выше|[Сс]реди|[Дд]ля|[Дд]о|' 481 r'[Ии]з|[Оо]т|[Бб]ез|[Уу]|[Вв]место|[Вв] возрасте|[Вв] размере|' 482 r'[Бб]лиже|[Вв] пределах|[Вв] течение|[Нн]а протяжении|' 483 r'[Нн]ач[инаетялсьо]{2,7} с|[Пп]орядка|[Пп]осле|[Пп]ротив|' 484 r'[Дд]остиг[авеийлнотшщюуья]{,5}|[Вв]ладел[аеимухцыь]{2,5}|' 485 r'[Сс]тарше|[Мм]оложе|не превы[шаеситьло]{3,4}' 486 r')' 487 r'( всех | следующих | целых | примерно | приблизительно ' 488 r'| почти | плюс | минус | )' 489 r'((\d+,|)(\d+)( - | или )|)(\d+,|)(\d+)' 490 r'( ([а-яё]+([иы]х|[ео]й|[ео]го) |и более |и менее |)' 491 r'([а-яё]{3,})|)\b') 492 for m in finditer(mask, text): 493 if m.group(3): 494 if m.group(4): 495 pre = fraction(m.group(4)[:-1], m.group(5), 1) 496 else: 497 pre = cardinal(m.group(5), r_ca) 498 if condition(m.group(5)) and m.group(12) is not None: 499 attr = words.get_attr(m.group(12)) 500 if m.group(9) and attr.have([Z_GENDER], None, [1]): 501 pre = pre[:-2] + 'й' 502 elif m.group(12) == 'суток': 503 pre = pre[:-3] + 'их' 504 pre += m.group(6) 505 else: 506 pre = '' 507 if m.group(7): 508 number = fraction(m.group(7)[:-1], m.group(8), 1) 509 else: 510 number = cardinal(m.group(8), r_ca) 511 if m.group(12): 512 attr = words.get_attr(m.group(12)) 513 if condition(m.group(8)) and attr.have(Z_GENDER, False, [1]): 514 number = number[:-2] + 'й' 515 elif m.group(12) == 'суток' and number[-6:] == 'одного': 516 number = number[:-3] + 'их' 517 new = m.group(1) + m.group(2) + pre + number + m.group(9) 518 text = text.replace(m.group(), new, 1) 519 520 # Предлог "с" + родительный падеж 521 mask = (r'\b([Сс]о? )((\d+)( [-и] | или )|)(\d+) ([а-яё]+)\b') 522 for m in finditer(mask, text): 523 attr = words.get_attr(m.group(6)) 524 if attr.have(None, None, [1]): 525 if m.group(2): 526 prenum = cardinal(m.group(3), r_ca) 527 if condition(m.group(3)) and attr.have([Z_GENDER], None, [1]): 528 prenum = prenum[:-2] + 'й' 529 prenum += m.group(4) 530 else: 531 prenum = '' 532 prenum = m.group(1) + prenum 533 number = cardinal(m.group(5), r_ca) 534 if attr.have([Z_GENDER], False, [1]): 535 number = number[:-2] + 'й' 536 new = prenum + number + ' ' + m.group(6) 537 text = text.replace(m.group(), new, 1) 538 539 mask = (r'(\A|\(| )((\d+) - |)(1|\d*[02-9]1)' 540 r'(( [а-яё]+[ео]го | )([а-яё]+))\b') 541 for m in finditer(mask, text): 542 attr = words.get_attr(m.group(7)) 543 a = attr.have([M_GENDER, S_GENDER], False, [1]) 544 b = attr.have([M_GENDER], False, [3]) 545 if a and not b: 546 number = cardinal(m.group(4), r_ca) 547 if m.group(2) == '': 548 pre = '' 549 else: 550 pre = cardinal(m.group(3), r_ca) 551 pre += ' - ' 552 new = m.group(1) + pre + number + m.group(5) 553 text = text.replace(m.group(), new, 1) 554 555 mask = (r'(\A|\(| )((\d+)( [-и] | или )|)(\d*[02-9][234]|[234])' 556 r'(( [а-яё]+[иы]х | )([а-яё]+))\b(.)') 557 for m in finditer(mask, text): 558 attr = words.get_attr(m.group(8)) 559 a = attr.have([M_GENDER, S_GENDER, Z_GENDER], True, [1]) 560 b = attr.have([M_GENDER], True, [3]) 561 if a and not b: 562 if m.group(2) == '': 563 number = '' 564 else: 565 number = cardinal(m.group(3), r_ca) + m.group(4) 566 if attr.have(gender=Z_GENDER) and number[-2:] == 'го': 567 number = number[:-2] + 'й' 568 new = (m.group(1) + number + cardinal(m.group(5), r_ca) + 569 m.group(6) + m.group(9)) 570 text = text.replace(m.group(), new, 1) 571 572 # Творительный падеж 573 # Исключение 574 mask = (r'\b((состав(ил[аио]?|[ия]т|ля[ею]т)|потеря(л[аио]?|[ею]т)) \d+) ' 575 r'(погибшими|ранеными|убитыми)' 576 r'(( и \d+) (погибшими|ранеными|убитыми)|)\b') 577 for m in finditer(mask, text): 578 if m.group(6): 579 new = m.group(7) + '_' + m.group(8) 580 else: 581 new = '' 582 new = m.group(1) + '_' + m.group(5) + new 583 text = text.replace(m.group(), new, 1) 584 585 mask = (r'(?<!\d,)\b(' 586 r'(\d+)' 587 r'( - | или | и (почти |приблизительно |примерно |плюс |минус |))|' 588 r')' 589 r'(\d+) ' 590 r'([а-яё]+([аиыья]ми|[ео]м|[еиоы]й|ью))\b') 591 for m in finditer(mask, text): 592 if m.group(1): 593 pre = cardinal(m.group(2), t_ca) 594 if condition(m.group(2)): 595 a = words.have(m.group(6), [Z_GENDER], False, [4]) 596 b = words.have(m.group(6)[:-2], [Z_GENDER], False, [0]) 597 c = words.have(m.group(6)[:-3] + 'ь', [Z_GENDER], False, [0]) 598 if a or b or c: 599 pre = pre[:-2] + 'ой' 600 pre += m.group(3) 601 else: 602 pre = '' 603 number = '' 604 if condition(m.group(5)): 605 attr = words.get_attr(m.group(6)) 606 if attr.have([M_GENDER, S_GENDER], False, [4]): 607 number = cardinal(m.group(5), t_ca) 608 elif attr.have([Z_GENDER], False, [4]): 609 number = cardinal(m.group(5), t_ca)[:-2] + 'ой' 610 elif m.group(6) == 'сутками': 611 number = cardinal(m.group(5), t_ca) + 'и' 612 elif m.group(6)[-2:] == 'ми': 613 number = cardinal(m.group(5), t_ca) 614 if number: 615 new = pre + number + ' ' + m.group(6) 616 text = text.replace(m.group(), new, 1) 617 618 # Предлоги творительного падежа 619 620 mask = (r'\b(([Мм]ежду|[Нн]ад|[Пп]еред|[Пп]о сравнению с) ' 621 r'(почти |приблизительно |примерно |плюс |минус |))' 622 r'((\d+,|)(\d+)' 623 r'( [-и] | или )' 624 r'(почти |приблизительно |примерно |плюс |минус |)|)' 625 r'(\d+,|)(\d+)\b') 626 for m in finditer(mask, text): 627 pre = m.group(1) 628 if m.group(4): 629 if m.group(5): 630 pre += fraction(m.group(5)[:-1], m.group(6), 3) 631 else: 632 pre += cardinal(m.group(6), t_ca) 633 pre = pre + m.group(7) + m.group(8) 634 if m.group(9): 635 number = fraction(m.group(9)[:-1], m.group(10), 3) 636 else: 637 number = cardinal(m.group(10), t_ca) 638 text = text.replace(m.group(), pre + number, 1) 639 640 # Предложный падеж 641 mask = (r'\b([Вв]|[Нн]а|[Оо]б?|[Пп]ри)' 642 r'(' 643 r'( почти | примерно | приблизительно | плюс | минус | )' 644 r'(\d+)( [-и] | или )| ' 645 r')' 646 r'(почти |примерно |приблизительно |плюс |минус |)(\d+)' 647 r'( ([а-яё]+([иы]х|[ео][йм]) |)([а-яё]+([ая]х|е|и|у)))\b') 648 for m in finditer(mask, text): 649 if m.group(2) == ' ': 650 pre = ' ' 651 else: 652 pre = m.group(3) + cardinal(m.group(4), p_ca) 653 a = words.have(m.group(11), None, False, [2, 5]) 654 b = words.have(m.group(11)[:-1] + 'м', [Z_GENDER], True, [2]) 655 if condition(m.group(4)) and (a or b): 656 pre = pre[:-1] + 'й' 657 elif m.group(11) == 'сутках': 658 pre = pre[:-2] + 'их' 659 pre += m.group(5) 660 number = '' 661 if m.group(12) == 'ах' or m.group(12) == 'ях': 662 number = cardinal(m.group(7), p_ca) 663 if condition(m.group(7)): 664 attr = words.get_attr(m.group(11)) 665 if attr.have([M_GENDER, S_GENDER], False, [5]): 666 number = cardinal(m.group(7), p_ca) 667 elif attr.have([Z_GENDER], False, [2, 5]): 668 number = cardinal(m.group(7), p_ca)[:-1] + 'й' 669 elif m.group(11) == 'сутках': 670 number = cardinal(m.group(7), p_ca)[:-2] + 'их' 671 elif m.group(12) == 'ах' or m.group(12) == 'ях': 672 number = cardinal(m.group(7), p_ca) 673 if number: 674 new = m.group(1) + pre + m.group(6) + number + m.group(8) 675 text = text.replace(m.group(), new, 1) 676 677 for m in finditer(r'\b(\d+) ([а-яё]+)\b', text): 678 attr = words.get_attr(m.group(2)) 679 a = attr.have(None, True, [5]) 680 b = condition(m.group(1)) 681 c = attr.have([M_GENDER, S_GENDER], False, [5]) 682 if a or (b and c): 683 new = cardinal(m.group(1), p_ca) + ' ' + m.group(2) 684 text = text.replace(m.group(), new, 1) 685 686 # Предлоги предложного падежа 687 mask = (r'\b([Оо]б?|[Пп]ри)' 688 r'( (\d+)( [-и] | или )| )(\d+)\b') 689 for m in finditer(mask, text): 690 number = ' ' 691 if m.group(2) != ' ': 692 number += cardinal(m.group(3), p_ca) + m.group(4) 693 new = m.group(1) + number + cardinal(m.group(5), p_ca) 694 text = text.replace(m.group(), new, 1) 695 696 # Винительный падеж 697 698 mask = (r'\b([Вв]|[Нн]а|[Зз]а|[Пп]ро|[Чч]ерез|состав[аеилотя]{2,4})' 699 r'( (\d+)( -| или)|) (\d+)' 700 r'(( [а-яё]+([ая]я|[ую]ю|[ео]е|[иы][йх]) | )([а-яё]+))\b') 701 for m in finditer(mask, text): 702 if m.group(2): 703 pre = cardinal(m.group(3), v_ca) 704 if pre[-3:] == 'дин': 705 attr = words.get_attr(m.group(9)) 706 if attr.have([M_GENDER], False, [3]): 707 pre = pre[:-2] + 'ного' 708 elif attr.have([Z_GENDER], False, [3]): 709 pre = pre[:-2] + 'ну' 710 elif attr.have([S_GENDER], False, [0, 3]): 711 pre = pre[:-2] + 'но' 712 pre += m.group(4) + ' ' 713 else: 714 pre = '' 715 number = cardinal(m.group(5), v_ca) 716 if number[-3:] == 'дин': 717 attr = words.get_attr(m.group(9)) 718 if attr.have([M_GENDER], False, [3]): 719 number = number[:-2] + 'ного' 720 elif attr.have([Z_GENDER], False, [3]): 721 number = number[:-2] + 'ну' 722 elif attr.have([S_GENDER], False, [0, 3]): 723 number = number[:-2] + 'но' 724 new = m.group(1) + ' ' + pre + number + m.group(6) 725 text = text.replace(m.group(), new, 1) 726 727 # Женский род (иминетельный/винительный падежи) 728 mask = (r'(\A|\(| )(((\d+)( - | или | и ))|)(\d+,|)(\d+)' 729 r'(( [а-яё]+([ая]я|[иы][ех])|) ([а-яё]+))') 730 for m in finditer(mask, text): 731 attr = words.get_attr(m.group(11)) 732 a = attr.have([Z_GENDER], None, [1]) 733 b = attr.have([Z_GENDER], False, [0]) 734 if (a or b): 735 new = m.group(1) 736 if m.group(2): 737 new += feminin(m.group(4)) + m.group(5) 738 if m.group(6): 739 new += m.group(6) + m.group(7) + m.group(8) 740 else: 741 new += feminin(m.group(7)) + m.group(8) 742 text = text.replace(m.group(), new, 1) 743 744# for m in finditer(r'\b(\d*[02-9]1|1)(( [а-яё]+[ео]го | )([а-яё]+))\b', text): 745# if (words.have(m.group(4), [M_GENDER], False, [3]) 746# and not words.have(m.group(4), [M_GENDER], False, [0])): 747# new = cardinal(m.group(1), v_ca)[:-2] + 'ного' + m.group(2) 748# text = text.replace(m.group(), new, 1) 749 750 # Вин. пад. муж. рода числительных, оканчивающихся на 1 кроме 11 751 mask = (r'(\s|\A|\(| )((\d+) - |)(1|\d*[02-9]1)' 752 r'(( [а-яё]+[ео]го | )([а-яё]+))\b') 753 for m in finditer(mask, text): 754 attr = words.get_attr(m.group(7)) 755 if attr.have([M_GENDER], False, [3]): 756 number = cardinal(m.group(4), v_ca)[:-2] + 'ного' 757 if m.group(2) == '': 758 pre = '' 759 else: 760 pre = cardinal(m.group(3), v_ca) 761 if condition(m.group(3)): 762 pre = pre[:-2] + 'ного' 763 elif pre[-3:] == 'два': 764 pre = pre[:-1] + 'ух' 765 elif pre[-3:] == 'три' or pre[-3:] == 'ыре': 766 pre = pre[:-1] + 'ёх' 767 pre += ' - ' 768 new = m.group(1) + pre + number + m.group(5) 769 text = text.replace(m.group(), new, 1) 770 # Вин. пад. жен. рода 771 for m in finditer(r'\b(\d*[02-9]1|1)(( [а-яё]+[ую]ю | )([а-яё]+))', text): 772 if words.have(m.group(4), [Z_GENDER], False, [3]): 773 new = cardinal(m.group(1), v_ca)[:-2] + 'ну' + m.group(2) 774 text = text.replace(m.group(), new, 1) 775 776 mask = (r'\b(\d*[02-9][2-4]|[2-4])' 777 r'(( [а-яё]+[иы]х | )([а-яё]+))') 778 for m in finditer(mask, text): 779 if words.have(m.group(4), [M_GENDER], True, [3]): 780 number = cardinal(m.group(1), v_ca) 781 if number[-3:] == 'два': 782 number = number[:-1] + 'ух' 783 else: 784 number = number[:-1] + 'ёх' 785 text = text.replace(m.group(), number + m.group(2), 1) 786 787 for m in finditer(r'\b([Вв] )(\d+)( раз[а]?)\b', text): 788 new = m.group(1) + cardinal(m.group(2), v_ca) + m.group(3) 789 text = text.replace(m.group(), new, 1) 790 791 # Средний род (именительный/винительный падежи) 792 for m in finditer(r'\b(\d*[02-9]1|1) (([а-яё]+[ео]е |)([а-яё]+[ео]))\b', text): 793 if words.have(m.group(4), [S_GENDER], False, [0, 3]): 794 if len(m.group(1)) > 1: 795 if int(m.group(1)[:-1]) != 0: 796 number = m.group(1)[:-1] + '0_одно' 797 else: 798 number = m.group(1)[:-1] + '_одно' 799 else: 800 number = m.group(1)[:-1] + 'одно' 801 text = text.replace(m.group(), number + ' ' + m.group(2), 1) 802 803 # Дательный падеж 804 mask = (r'(?<!-)\b((\d+)( [-и] | или )|)(\d+)' 805 r'(( [а-яё]+([иы]м|[ео]му) | )([а-яё]+([аиыя]м|у|ю|е)))\b') 806 for m in finditer(mask, text): 807 if m.group(1) == '': 808 pre = '' 809 else: 810 pre = ' ' + cardinal(m.group(2), d_ca) 811 attr = words.get_attr(m.group(8)) 812 a = attr.have([Z_GENDER], None, [2]) 813 b = attr.have([Z_GENDER], False, [5]) 814 if condition(m.group(2)) and (a or b): 815 pre = pre[:-2] + 'й' 816 elif m.group(8) == 'суткам': 817 pre = pre[:-3] + 'им' 818 pre += m.group(3) 819 number = '' 820 if condition(m.group(4)): 821 if words.have(m.group(8), [M_GENDER, S_GENDER], False, [2]): 822 number = cardinal(m.group(4), d_ca) 823 elif words.have(m.group(8), [Z_GENDER], False, [2, 5]): 824 number = cardinal(m.group(4), d_ca)[:-2] + 'й' 825 elif m.group(8) == 'суткам': 826 number = cardinal(m.group(4), d_ca)[:-3] + 'им' 827 elif m.group(9) == 'ам' or m.group(9) == 'ям': 828 number = cardinal(m.group(4), d_ca) 829 if number: 830 text = text.replace(m.group(), pre + number + m.group(5), 1) 831 832 # Предлоги дательного падежа 833 mask = (r'\b((\A|\n|\(| )[Кк]|рав[нагеийлмоcуюыхья]{2,6})' 834 r'( (\d+)( [-и] | или )| )(\d+)\b') 835 for m in finditer(mask, text): 836 number = ' ' 837 if m.group(3) != ' ': 838 number += cardinal(m.group(4), d_ca) + m.group(5) 839 new = m.group(1) + number + cardinal(m.group(6), d_ca) 840 text = text.replace(m.group(), new, 1) 841 842 # Существует только во множественном числе 843 for m in finditer(r'\b((\d+) - |)((\d+) (сутки|суток))', text): 844 if m.group(1): 845 pre = daynight(m.group(2), m.group(5)) + '-' 846 else: 847 pre = '' 848 new = pre + daynight(m.group(4), m.group(5)) + ' ' + m.group(5) 849 text = text.replace(m.group(), new, 1) 850 851 # Предлог "по" при указании количества 852 for m in finditer(r'\b([Пп]о )(\d*1(000){1,3})\b', text): 853 new = m.group(1) + cardinal(m.group(2), d_ca) 854 text = text.replace(m.group(), new, 1) 855 856 # Десятичные дроби в именительном падеже 857 for m in finditer(r'\b(\d+),(\d+)(\b|\Z)', text): 858 new = fraction(m.group(1), m.group(2)) + m.group(3) 859 text = text.replace(m.group(), new, 1) 860 861 # Например: "все небо" -> "всё небо" 862 for m in finditer(r'\b([Вв]с)е ([а-яё]+)\b', text): 863 if words.have(m.group(2), [S_GENDER], False, [0, 3]): 864 text = text.replace(m.group(), m.group(1) + 'ё ' + m.group(2), 1) 865 866 # Буквы греческого алфавита 867 for j in greekletters: 868 text = text.replace(j, letternames[greekletters.index(j)//2], 1) 869 870 # Окончателная обработка 871 for sample in samples_4: 872 text = sub(sample[0], sample[1], text) 873 874 return text 875