import regex from num2words import num2words import unicodedata simple_replacements = { '№' : 'номер', '§': 'номер' } masc_replacments_dict = { '%':['відсоток', 'відсотки', 'відсотків'], 'мм': ['міліметр', 'міліметри', 'міліметрів'], 'см': ['сантиметр', 'сантиметри', 'сантиметрів'], 'мм': ['міліметр', 'міліметри', 'міліметрів'], # 'м': ['метр', 'метри', 'метрів'], 'км': ['кілометр', 'кілометри', 'кілометрів'], 'гц': ['герц', 'герци', 'герців'], 'кгц': ['кілогерц', 'кілогерци', 'кілогерців'], 'мгц': ['мегагерц', 'мегагерци', 'мегагерців'], 'ггц': ['гігагерц', 'гігагерци', 'гігагерців'], 'вт': ['ват', 'вати', 'ватів'], 'квт': ['кіловат', 'кіловати', 'кіловатів'], 'мвт': ['мегават', 'мегавати', 'мегаватів'], 'гвт': ['гігават', 'гігавати', 'гігаватів'], 'дж': ['джоуль', 'джоулі', 'джоулів'], 'кдж': ['кілоджоуль', 'кілоджоулі', 'кілоджоулів'], 'мдж': ['мегаджоуль', 'мегаджоулі', 'мегаджоулів'], 'см2': ['сантиметр квадратний', 'сантиметри квадратні', 'сантиметрів квадратних'], 'м2': ['метр квадратний', 'метри квадратні', 'метрів квадратних'], 'м2': ['кілометр квадратний', 'кілометри квадратні', 'кілометрів квадратних'], '$': ['долар', 'долари', 'доларів'], '€': ['євро', 'євро', 'євро'], } fem_replacments_dict = { 'кал': ['калорія', 'калорії', 'калорій'], 'ккал': ['кілокалорія', 'кілокалорії', 'кілокалорій'], 'грн': ['гривня', 'гривні', 'гривень'], 'грв': ['гривня', 'гривні', 'гривень'], '₴': ['гривня', 'гривні', 'гривень'], } neu_replacments_dict = { '€': ['євро', 'євро', 'євро'], } all_replacments_keys = list(masc_replacments_dict.keys()) + list(fem_replacments_dict.keys()) + list(neu_replacments_dict.keys()) #Ordinal types #Називний ordinal_nominative_masculine_cases = ('й','ий') ordinal_nominative_feminine_cases = ('a','ша', 'я') ordinal_nominative_neuter_cases = ('е',) #Родовий ordinal_genitive_masculine_case = ('го','о',) ordinal_genitive_feminine_case = ('ї', 'ої') #Давальний ordinal_dative_masculine_case = ('му',) ordinal_dative_feminine_case = ('й','ій') #Знахідний ordinal_accusative_masculine_case = ordinal_genitive_masculine_case ordinal_accusative_feminine_case = ('у',) #Орудний ordinal_instrumental_masculine_case = ('им', 'ім') ordinal_instrumental_feminine_case = ('ю') #Місцевий # ordinal_locative_masculine_case = ordinal_dative_masculine_case # ordinal_locative_feminine_case = ordinal_dative_feminine_case numcases_r = regex.compile(rf'((?:^|\s)(\d+)\s*(\-?)(([^\d,]*?)|(\-\.+))(?:\.|,|:|-)?)(\s+[^,.:\-]|$)', regex.IGNORECASE, regex.UNICODE) print(numcases_r) cardinal_genitive_endings = ('а', 'e', 'є', 'й') ordinal_genitive_cases = ('року',) def number_form(number): if number[-1] == "1": return 0 elif number[-1] in ("2", "3", "4"): return 1 else: return 2 def replace_cases(number, dash, case='', next_word=''): print(f'{number}, {dash}, {case}, {next_word}') gender = 'masculine' m_case = 'nominative' to = 'ordinal' repl = '' if not dash: if case in all_replacments_keys: if case in masc_replacments_dict.keys(): repl = masc_replacments_dict.get(case)[number_form(number)] gender = 'masculine' elif case in fem_replacments_dict.keys(): repl = fem_replacments_dict.get(case)[number_form(number)] gender = 'feminine' elif case in neu_replacments_dict.keys(): repl = neu_replacments_dict.get(case)[number_form(number)] gender = 'neuter' to = 'cardinal' else: if len(case) < 3 and case and case[-1] in cardinal_genitive_endings: m_case = 'genitive' gender='masculine' to = 'cardinal' elif case in ordinal_genitive_cases: to = 'ordinal' m_case = 'genitive' repl = case else: to = 'cardinal' repl = case else: if case in ordinal_nominative_masculine_cases: m_case = 'nominative' gender = 'masculine' elif case in ordinal_nominative_feminine_cases: m_case = 'nominative' gender = 'feminine' elif case in ordinal_nominative_neuter_cases: m_case = 'nominative' gender = 'neuter' elif case in ordinal_genitive_masculine_case: m_case = 'genitive' gender = 'masculine' elif case in ordinal_genitive_feminine_case: m_case = 'genitive' gender = 'feminine' elif case in ordinal_dative_masculine_case: m_case = 'dative' gender = 'masculine' elif case in ordinal_dative_feminine_case: m_case = 'dative' gender = 'feminine' elif case in ordinal_accusative_feminine_case: m_case = 'accusative' gender = 'feminine' elif case in ordinal_instrumental_masculine_case: m_case = 'instrumental' gender = 'masculine' elif case in ordinal_instrumental_feminine_case: m_case = 'instrumental' gender = 'feminine' else: if case and case[-1] in cardinal_genitive_endings: m_case = 'genitive' gender='masculine' to = 'cardinal' repl = case else: print(f'UNKNOWN CASE {number}-{case}') return_str = num2words(number, to=to, lang='uk', case=m_case, gender=gender) if repl: return_str += ' ' + repl if not next_word or (next_word and next_word.strip().isupper()): return_str += '.' return return_str def norm(text): text = regex.sub(r'[\t\n]', ' ', text) text = regex.sub(rf"[{''.join(simple_replacements.keys())}]", lambda x: f' {simple_replacements[x.group()]} ', text) text = regex.sub(r"(\d)\s+(\d)", r"\1\2", text) text = regex.sub(r'\s+', ' ', text) text = unicodedata.normalize('NFC', text) matches = numcases_r.finditer(text) pos = 0 new_text = '' for m in matches: repl = replace_cases(m.group(2), m.group(3), m.group(4), m.group(7)) new_text += text[pos:m.start(0)]+ ' ' + repl pos = m.end(1) new_text += text[pos:] return new_text.strip() #1-го квітня, на 1-му поверсі Яринка загубила 2грн але знайшла 5€. Але її 4-річна сестричка забрала 50% її знахідки. #Також 2003 року щось там сталося і 40-річний чоловік помер. Його знайшли через 3 години. #01:51:37.250 -> 01:51:44.650: Серед міленіалів цей показник становить 39%, серед покоління X – 30%, #39 #30 #MATCHED: серед міленіалів цей показник становить тридцять девять , серед покоління Х - тридцять , #Skipped because contains inapropirate characters #05:28:52.350 -> 05:29:00.000: 2016 рік завершився з чистими збитками 1,2 мільярди доларів США. #2016 #MATCHED: дві тисячі шістнадцять рік завершився з чистими збитками 1,2 млрд доларів США.