import re U=["ноль","один","два","три","четыре","пять","шесть","семь","восемь","девять","десять","одиннадцать","двенадцать","тринадцать","четырнадцать","пятнадцать","шестнадцать","семнадцать","восемнадцать","девятнадцать"] T=["","","двадцать","тридцать","сорок","пятьдесят","шестьдесят","семьдесят","восемьдесят","девяносто"] H=["","сто","двести","триста","четыреста","пятьсот","шестьсот","семьсот","восемьсот","девятьсот"] def card(n): if n<20: return U[n] if n<100: return (T[n//10]+(" "+U[n%10] if n%10 else "")) if n<1000: return (H[n//100]+(" "+card(n%100) if n%100 else "")) if n<1000000: th,r=divmod(n,1000); t=th%10; tt=th%100 w=card(th) if th>2 or th>=20 else "" if th==1: w="одна тысяча" elif th==2: w="две тысячи" else: w=card(th) w=re.sub(r"один$","одна",w); w=re.sub(r"два$","две",w) w+=" "+("тысяча" if t==1 and tt!=11 else "тысячи" if 2<=t<=4 and not 12<=tt<=14 else "тысяч") return w+(" "+card(r) if r else "") return str(n) ORD={"ноль":"нулев","один":"перв","два":"втор","три":"трет","четыре":"четвёрт","пять":"пят","шесть":"шест","семь":"седьм","восемь":"восьм","девять":"девят","десять":"десят","одиннадцать":"одиннадцат","двенадцать":"двенадцат","тринадцать":"тринадцат","четырнадцать":"четырнадцат","пятнадцать":"пятнадцат","шестнадцать":"шестнадцат","семнадцать":"семнадцат","восемнадцать":"восемнадцат","девятнадцать":"девятнадцат","двадцать":"двадцат","тридцать":"тридцат","сорок":"сороков","пятьдесят":"пятидесят","шестьдесят":"шестидесят","семьдесят":"семидесят","восемьдесят":"восьмидесят","девяносто":"девяност","сто":"сот","двести":"двухсот","триста":"трёхсот","четыреста":"четырёхсот","пятьсот":"пятисот","шестьсот":"шестисот","семьсот":"семисот","восемьсот":"восьмисот","девятьсот":"девятисот","тысяча":"тысячн","тысячи":"тысячн","тысяч":"тысячн"} # ending by case/gender: suffix of written form -> ending (hard stem / трет soft) END={"й":"ый","го":"ого","му":"ому","м":"ом","е":"ое","я":"ая","ю":"ую","х":"ых","ми":"ыми"} def ordinal(n,suf): ws=card(n).split(); last=ws[-1]; stem=ORD.get(last,last) e=END.get(suf,"ый") if stem=="трет": e={"ый":"ий","ого":"ьего","ому":"ьему","ом":"ьем","ое":"ье","ая":"ья","ую":"ью","ых":"ьих","ыми":"ьими"}[e] elif stem in("втор","сороков") or stem.endswith(("тысячн",)): e=e.replace("ый","ой") if stem=="втор" or stem=="сороков" else e if e=="ый" and stem in("втор","шест","седьм","восьм","сороков"): e="ой" return " ".join(ws[:-1]+[stem+e]) def normalize(t): t=t.replace("\xa0"," ").replace("•","").replace("*","").replace("_"," ").replace("–",",").replace("№","номер ") t=re.sub(r"(\d+)%",lambda m:card(int(m[1]))+" процентов",t) t=re.sub(r"\b(\d+)-(й|го|му|м|е|я|ю|х|ми)\b",lambda m:ordinal(int(m[1]),m[2]),t) t=re.sub(r"\b(\d+)\s+(году|года|год|годом)\b",lambda m:ordinal(int(m[1]),{"году":"м","года":"го","год":"й","годом":"м"}[m[2]])+" "+m[2],t) t=re.sub(r"^\s*(\d+)\.\s",lambda m:card(int(m[1]))+". ",t,flags=re.M) t=re.sub(r"\d+",lambda m:card(int(m[0])),t) t=re.sub(r"[ \t]{2,}"," ",t) return t if __name__=="__main__": for s in ["в 508 году","3-й ярус","341-го дня","34% урожая","1200 монет","1. Первое","до 507 года","2-ю неделю","3-е место","21 кролик"]: print(s,"->",normalize(s))