Introdução
Após a criação do post [1], percebi que muitas pessoas acessam o post procurando por uma implementação de gerar números por extenso em C. Um código em java [2], também é muito procurado. Com isso, resolvi criar uma biblioteca em C bastante simples que gera o extenso de números naturais.
Só postarei o código e a forma de uso. Detalhes de implementação se encontram no código e também em [1].
O código
São dois arquivos: o .h e o .c. Eles devem ser salvos no mesmo diretório.
Primeiro o .h:
/* * [dextenso.h] * Cabeçalho que deve ser incluído em seu código fonte. * Arqui está as definições das variáveis globais e das * funções usadas. * * [Autor] * Marcos Paulo Ferreira (Daemonio) * undefinido gmail com * https://daemoniolabs.wordpress.com * * Versão 1.0 by daemonio @ Fri Oct 19 22:45:06 BRT 2012 * */ #ifndef _DEXTENSO_H #define _DEXTENSO_H #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> /* for isdigit() */ // dextenso.c #define EXTENSO_TAMANHO 4069 /* tamanho máximo que um extenso tem. */ #define SAIDA_TAMANHO (20+20*3+5*3) struct dextensost { char *entrada ; char *extenso ; int pos ; char trio[3] ; } ; extern char *matrizextenso[4][10] ; extern char *classextenso[] ; static int dext_obter_trio_ordem_inversa(struct dextensost *dext) ; static void dext_escrever_trio_extenso(char *saida, char trio[3]) ; struct dextensost *dext_init(char *snum) ; static void insert_string_at(char *s, char *t, int n) ; static int trio_a_esquerda_eq_zero(char *dnumeroentrada, unsigned int contador) ; static int dext_nao_e_ultimo_trio(char *entrada, unsigned int contador) ; int dext_obter_extenso_de(struct dextensost *dext) ; void dext_close(struct dextensost *dext) ; #endif
Agora, o .c:
/* * [dextenso.c] * Implementações da biblioteca. O código está * parcialmente comentado, pois a maioria dos * detalhes principais seguem o algoritmo * geral visto no blog. * * [Autor] * Marcos Paulo Ferreira (Daemonio) * undefinido gmail com * https://daemoniolabs.wordpress.com * * Versão 1.0 by daemonio @ Fri Oct 19 22:45:06 BRT 2012 * */ #include "dextenso.h" char *matrizextenso[4][10] = { {"dummy", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove"}, {"dez", "onze", "doze", "treze", "quatorze", "quinze", "dezesseis", "dezessete", "dezoito", "dezenove"}, {"dummy", "dummy", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa"}, {"dummy", "cento", "duzentos", "trezentos", "quatrocentos", "quinhentos", "seiscentos", "setecentos", "oitocentos", "novecentos"} }; char *classextenso[] = { "dummy", "mil", "milh", "bilh", "trilh", "quatrilh", "quintilh", "sextilh", "septilh", "octilh", "nonilh", "decilh", "undecilh", "duodecilh", "tredecilh", "quatordecilh", "quindecilh", "sexdecilh", "setedecilh", "octodecilh", "novedecilh" "vigesilh", NULL } ; /* Obtém os trios em ordem inversa. A função retorna '1' quando há * trios para retornar. O retorno '0' indica fim do processo. * * As variáveis pos e trio da struct dextensost são utilizadas * somente aqui. */ static int dext_obter_trio_ordem_inversa(struct dextensost *dext) { if(dext->pos > 0) { dext->trio[2] = dext->entrada[dext->pos ] ; dext->trio[1] = dext->entrada[dext->pos-1] ; dext->trio[0] = dext->entrada[dext->pos-2] ; dext->pos -= 3; return 1 ; } return 0 ; } /* Retorna um trio por extenso. O trio vem no segundo parâmetro, * e o extenso é salvo na variável saida. */ static void dext_escrever_trio_extenso(char *saida, char trio[3]) { char c = trio[0] ; char d = trio[1] ; char u = trio[2] ; int i ; saida[0]=0; if(! strncmp(trio, "100", 3) ) { strcpy(saida, "cem") ; } else if(! strncmp(trio, "000", 3)) { strcpy(saida, "zero") ; } else { if(c != '0') sprintf(saida, "%s", matrizextenso[3][c-'0']) ; if(d == '1') { sprintf(saida, "%s e %s", saida, matrizextenso[1][u-'0']) ; } else { if(d != '0') { sprintf(saida, "%s e %s", saida, matrizextenso[2][d-'0']) ; } if(u != '0') { sprintf(saida, "%s e %s", saida, matrizextenso[0][u-'0']) ; } } } /* Corrige as conjunções 'e' para algumas saídas. * Esse if seria parte da função adicionar_conjuncao_e() * descrita no algoritmo. */ if(saida[0] == ' ') { //strcpy(saida, saida+3) ; deu bug com isso... i=0; while(saida[i+3]!=0x0) { saida[i]=saida[i+3]; i++; } saida[i]=0 ; } } /* Inicia a biblioteca. Essa função deve sempre * ser chamada antes de gerar algum extenso. * * Ela recebe como entrada o número natural e retorna * uma struct dextensost que engloba campos necessários * para a execução do algoritmo. */ struct dextensost *dext_init(char *snum) { int i, pad, len ; char *e, *f ; struct dextensost *dext ; /* Devemos tratar somente números. */ for(i=0; snum[i]; i++) { if(!isdigit(snum[i])) return NULL ; } /* Obtém memória para a struct */ dext = (struct dextensost*)malloc(sizeof(struct dextensost)) ; if(dext == NULL) { return NULL ; } /* Normaliza o número, tornando sua quantidade de * algarismo múltipla de três. */ while(*snum=='0')snum++ ; if(*snum==0x0)snum="000" ; len=strlen(snum) ; pad=len%3 ? 3-len%3 : 0; if((e = malloc(strlen(snum)+pad+1))==NULL) { return NULL ; } memset(e, '0', pad) ; strcpy(e+pad, snum) ; /* Obtém memória para o extenso final. * Aqui calculamos um tamanho razoável para * o extenso de saída, porém, esse tamanho * nunca deve exceder EXTENSO_TAMANHO. */ len = (50*(strlen(e)/3)) ; len = len > EXTENSO_TAMANHO ? EXTENSO_TAMANHO : len ; f=(char *)malloc(len) ; if(f==NULL) { return NULL ; } f[0]=0; /* Inicializa campos da struct. */ dext->entrada = e ; dext->extenso = f ; dext->pos = strlen(e)-1 ; return dext; } /* * Insere a string `t' na posição n de `s'. Essa função * arreda primeiramente o vetor `s' para à direita para depois * adicionar `t'. */ void insert_string_at(char *s, char *t, int n) { int i = strlen(s) ; int j = strlen(t) ; s[i+j]=0 ; if(n<i) { while(i--) { s[i+j] = s[i]; } } strncpy(s+n, t, j) ; } /* Retorna verdadeiro se o trio à esquerda é * igual a zero (000). */ static int trio_a_esquerda_eq_zero(char *dnumeroentrada, unsigned int contador) { int t ; t = strlen(dnumeroentrada)- 3 - 3*(contador+1) ; return !strncmp(dnumeroentrada+t, "000", 3) ; } /* Retorna verdadeiro se o trio atual não é * o último. */ static int dext_nao_e_ultimo_trio(char *entrada, unsigned int contador) { return contador<((strlen(entrada)/3)-1) ; } /* Função pública que deve ser usada para gerar * o extenso. Ela recebe uma struct dextensost e * retorna o extenso na própria estrutura. * * Ela retorna 0 se o número dado é muito grande, e * nesse caso, o extenso de saída estará inconsistente. * O valor 1 é retornado se tudo ocorreu sem erros. */ int dext_obter_extenso_de(struct dextensost *dext) { char saida[SAIDA_TAMANHO] ; unsigned int contador = 0; /* Obtém o tamanho limite do vetor de classes * para evitar acessos fora da área de memória * para números grandes. */ unsigned int limit = 0; while(classextenso[limit]!=NULL)limit++ ; /* Inicia o algoritmo. */ while(dext_obter_trio_ordem_inversa(dext)) { if(contador > limit) return -1 ; saida[0]=0 ; /* se trio > 000 */ if(strncmp(dext->trio, "000", 3)) { dext_escrever_trio_extenso(saida, dext->trio); if(contador > 0) { sprintf(saida, "%s %s", saida, classextenso[contador]) ; } if(contador > 1) { if(strncmp(dext->trio,"000", 3) && strncmp(dext->trio,"001", 3)) { strcat(saida, "ões") ; } else { strcat(saida, "ão") ; } } if(dext_nao_e_ultimo_trio(dext->entrada, contador)) { if(trio_a_esquerda_eq_zero(dext->entrada, contador)) { insert_string_at(saida, " e ", 0); } else if(dext->trio[0] != '0') { insert_string_at(saida, ", " , 0); } else { insert_string_at(saida, " e ", 0); } } /* extensofinal = saida + extensofinal */ insert_string_at(dext->extenso, saida, 0) ; } contador = contador + 1 ; } /* Zero. */ if(dext->extenso[0]==0x0) { strcpy(dext->extenso, "zero") ; } return 1 ; } /* Função que deve ser chamada para liberar memória * alocada. */ void dext_close(struct dextensost *dext) { free(dext->entrada) ; free(dext->extenso) ; free(dext) ; }
Exemplo de utilização
Agora vamos criar um código que utiliza essa biblioteca.
/* [dex.c] * Código exemplo para testar a biblioteca * dextenso. * * [Autor] * Marcos Paulo Ferreira (daemonio) * undefinido gmail com * https://daemoniolabs.wordpress.com * * Versão 1.0 by daemonio @ Fri Oct 19 23:06:25 BRT 2012 */ #include <stdio.h> /* Inclusão do cabeçalho. Os arquivos * dextenso.h e dextenso.c devem estar no * mesmo diretório que esse fonte. */ #include "dextenso.h" int main(int argc, char **argv) { struct dextensost *dext ; if(argc < 2) { printf("[uso] %s <numero>\n", argv[0]) ; return -1 ; } /* Inicia a biblioteca. O parâmetro é o * número a ser convertido. Nesse caso, ele * vem da linha de comando. */ dext = dext_init(argv[1]) ; if(dext == NULL) { printf("[+] Erro: Impossível tratar número de entrada.\n") ; return -1 ; } /* Gera o extenso do número passado. */ if(dext_obter_extenso_de(dext) < 0) { printf("[+] Erro: Número de entrada muito grande.\n") ; } /* Mostra o extenso na tela. */ printf("%s\n", dext->extenso) ; /* Libera memória. */ dext_close(dext) ; return 0 ; }
Compilando:
$ gcc -o dex dex.c dextenso.c
Usando:
$ ./dex 10 dez $ ./dex 1989 um mil, novecentos e oitenta e nove $ ./dex 10451878874504014 dez quatrilhões, quatrocentos e cinquenta e um trilhões, oitocentos e setenta e oito bilhões, oitocentos e setenta e quatro milhões, quinhentos e quatro mil e quatorze $ ./dex 99999999 noventa e nove milhões, novecentos e noventa e nove mil, novecentos e noventa e nove $ ./dex aaaaaa [+] Erro: Impossível tratar número de entrada.
Referências
[1] Números Por Extenso: Algoritmo Geral by daemonio (Acessado em: Outubro/2012)
https://daemoniolabs.wordpress.com/2012/06/24/numeros-por-extenso-algoritmo-geral/
[2] Código De Número Por Extenso Em Java by daemonio (Acessado em: Outubro/2012)
https://daemoniolabs.wordpress.com/2012/07/05/codigo-de-numero-por-extenso-em-java/
Corrigido um erro do wordpress que não mostrava o caractere nulo como \ (barra) zero. Troquei todas as ocorrências de barra zero por 0x0;
Pingback: Classe Python de Números Por Extenso | Daemonio Labs