e-mail    Debatní kniha    Mapa stránek    Hlavní  
 perličky 
 

Převod čísla na text

Mgr. Jiří Zelinka

Při psaní programu pro tisk složenek jsem narazil na potřebu vytisknout danou částku i jako text, tedy například číslo 154 vytisknout na složence jako text sto padesát čtyři. Nevím, jestli tenhle problém řešili i v Bajtu, jak by se zdálo podle složenky na předplatné, která mi z redakce došla. Na všech složenkách mohlo být totiž totéž číslo jako na mé.

Ale zpátky ke zmíněnému problému. V rámci jeho řešení mě napadla jedna finta, s pomocí které se dá problém řešit velmi efektně. Nejprve ale několik zmínek o mluvnické stránce celé záležitosti. Pokud se někde dopustím chyby, nechť mi jazykovědci prominou.

Číslovky mohou mít v češtině tři rody, které se ovšem prakticky projeví jen u číslovek 1 (jeden, jedna, jedno), 2 (dva, dvě, dvě) či u číslovek, které těmito číslicemi končí, např. 31, 52 apod. U ostatních jsou tvary stejné. Když řeknete deset, nepozná se, jestli se to týká mužů nebo žen.

Dále se podle číslovky řídí pád slova, následujícího za ní, což se ale týká i slov, ze kterých je složena číslovka samotná. Je tedy pět tisíc, ale čtyři tisíce. Zde je ovšem výjimka u stovek. Správný je tvar dvě stě na rozdíl od tři sta -- je to tzv. dvojné číslo, pozůstatek z minulosti. Taky se zpravidla nepoužívá tvar jedno sto, ale jen sto. A pak je nutno pamatovat i na to, že po číslech mezi desítkou a dvacítkou je tentýž tvar jako třeba po čísle třicet.

Při vlastní realizaci algoritmu jsem se tedy rozhodl všechny tvary poskládat do pole tak, aby počet daných jednotek odpovídal indexu příslušného tvaru. Např. druhý prvek pole obsahujícího možné tvary stovek je řetězec stě, třetí pak je sta atp. Tak stačí skládat jednotlivé tvary správným způsobem za sebe. Vzhledem k tomu, že jsem převodní funkci psal v céčku, ukázalo se velmi vhodné umístit do nultého prvku prázdný řetězec, protože nulové množství se neuvádí, tj. nepíše se třeba nula set. Ovšem s výjimkou tisíců a milionů, protože zde se mohou vyskytovat jejich desítky a sta na rozdíl od desítek a stovek samotných.

Protože nevím, jak je v céčku interpretován výraz "", prázdný řetězec jsem definoval explicitně jako řetězec s nulovým znakem na začátku. Všechna pole obsahující možné tvary jednotlivých číslovek je samozřejmě nejlepší deklarovat jako statická, aby se zabránilo opětné inicializaci při novém volání funkce. Většina položek v každém poli je shodných, takže v rámci šetření paměti je vhodné zapnout volbu, při níž kompilátor ukládá stejné konstantní řetězce na jedno místo, nebo provést explicitní přiřazení při prvním volání funkce místo mnou uváděné inicializace, kterou jsem použil kvůli lepší čitelnosti programu.

Zefektivnit vlastní algoritmus lze dále tím, že např. čísla 135000 a 135 se liší pouze ve slově tisíc, které je při přepisu do textu na konci prvního z nich. Podobně je tomu u miliónů a u vyšších mocnin tisíce. Je tedy účelné číslovku rozdělit do skupin vždy po třech číslicích, na každou aplikovat stejný algoritmus a v případě potřeby připojit příslušné označení řádu skupiny.

Teď už k výše zmíněné fintě, která možná někomu přijde jako samozřejmost. Problém je v tom, že řetězce se lépe skládají z levé strany, kdežto jednotlivé číslice daného čísla se dobře zjišťují ze strany opačné. U čísla 548 snadno zjistíte, že končí osmičkou, ale hůř se zjišťuje, čím a na kterém místě začíná. Při řešení tohoto protikladu mezi levicí a pravicí mě nejprve napadlo jít na to přes logaritmus, pak zjistit začátek pomocí postupného dělení 10, nebo převést číslo na řetězec číslic, ale pořád to nebylo to pravé ořechové. Až nakonec mě napadlo řešení vpravdě dialektické (ne nadarmo mám za sebou téměř dva měsíce přednášek z marxismu-leninismu). Budu řetězec skládat z levé strany, ale přitom z pravé. Jak to udělat? Jednoduše -- jednotlivá slova, ze kterých je řetězec složen, zrcadlově otočím, a pak z nich poskládám výsledný řetězec, který nakonec otočím zase nazpět. V Turbo C je na to dokonce knihovní funkce strrev(). Jednotlivá slova tímto postupem ztratí sice poněkud na čitelnosti, ale co by člověk neobětoval pro jednoduchost zápisu algoritmu.

A to je vlastně skoro všechno. Ještě bych poznamenal, že pro tisk na složenky je třeba vypustit mezery mezi slovy. Funkce pracuje pouze s celými čísly, převádí čísla jen do prvního pádu, nevytváří tvary jako třeba dvanáct set a předpokládá číslo menší než jedna miliarda. Posledně uvedené lze snadno odstranit, princip je stejný jako pro tisíce a milióny až na to, že miliarda je ženského rodu. Pro spojování řetězců jsem použil knihovní funkci strcat(), pro kopírování funkci strcpy(). A jestli některý zkušený céčkař najde v algoritmu nějaké neefektivnosti, tak jsem se snažil spíš o snadnou pochopitelnost (i pro pojídače koláčů).

#include <string.h> typedef enum { MUZ, ZEN, STR} RODY; char *num2text(unsigned long cislo char *vystup, RODY rod) { /* koncovky pro jednotlivé rody u čísel 1 a 2 */ static char *koncovky[3][3] = { {"\0","\0","\0"},{"ne","an","on"},{"a","ě","ě"}}; /* tvary jednotek, převráceně, */ /* jedna a dvě jsou bez koncovky */ static char *jednotky[20] = {"\0","dej ","vd ","iřt ","iřytč ","těp ", "tseš ","mdes ","mso ","těved ","tesed ", "tcánedej ","tcánavd ","tcániřt ", "tcánrtč ","tcántap ","tcántseš ", "tcánmdes ","tcánmso ","tcánetaved "}; /* tvary desítek, převráceně */ static char *desitky[10] = {"\0","tesed ","tecavd ","teciřt ", "teciřytč ","tásedap ","tásedeš ", "tásedmdes ","tásedmso ","tásedaved "}; /* tvary stovek, převráceně */ static char *stovky[10] = {"\0","ots ","ěts ","ats ","ats ", "tes ","tes ","tes ","tes ","tes "}; /* tvary tisíců, převráceně */ static char *tisice[20] = {"císit ","císit ","ecísit ","ecísit ","ecísit ", "císit ","císit ","císit ","císit ","císit ", "císit ","císit ","císit ","císit ","císit ", "císit ","císit ","císit ","císit ","císit "}; /* tvary milionů, převráceně */ static char *miliony[20] = {"ůnoilim ","noilim ","ynoilim ","ynoilim ","ynoilim ", "ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim ", "ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim ", "ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim ","ůnoilim "}; char text[3][30]; /* řetězce pro jednotlivé skupiny */ unsigned int c[3]; /* čísla pro jednotlivé skupiny */ unsigned int p_j, p_d, p_s; /* počet jednotek, desítek a stovek */ int i; /* počáteční vytvoření jednotlivých skupin po třech číslicích */ c[0] = (unsigned int) (cislo % 1000); cislo /= 1000; c[1] = (unsigned int) (cislo % 1000); c[2] = (unsigned int) (cislo / 1000); for(i=0; i<3; i++) if(c[i]>0) { text[i][0]='\0'; p_d=0; if( (p_j=c[i] % 100) > 19) { p_j=c[i] % 10; /* provádí se, pokud skupina */ p_d=c[i]/10; /* nekončí číslem mezi 01 až */ p_d %= 10; /* 19, jinak je počet desítek */ } /* nulový */ p_s=c[i]/100; if(i==1 && c[i]!=0) /* připojení slova tisíc(e) */ { strcpy(text[i],tisice[p_j]); rod=MUZ; } if(i==2 && c[i]!=0) /* připojení slova milion(y/ů) */ { strcpy(text[i],miliony[p_j]); rod=MUZ; } if(p_j<=2) /* připojení koncovky podle rodu */ strcat(text[i],koncovky[p_j][rod]); strcat(text[i],jednotky[p_j]); /* skládání */ strcat(text[i],desitky[p_d]); /* textu */ strcat(text[i],stovky[p_s]); if(p_s==2) /* připojení koncovky pro stovky */ strcat(text[i],koncovky[2][STR]); if(p_s != 1) /* počet stovek se nepíše u jednoho sta */ strcat(text[i],jednotky[p_s]); } vystup[0]='\0'; for(i=0;i<3;i++) /* spojení všech tří skupin */ if(c[i]>0) strcat(vystup,text[i]); strrev(vystup); /* otočení hotového řetězce */ if(!strlen(vystup)) strcpy(vystup," nula"); return vystup;


Pascal - hlavní
Překladače
Vlastní články
Převzaté články
Věci na stáhnutí
Odkazy k tématu
BP7 buglist
Chyba Run-time 200

BASIC