Biblioteca em C: Números por Extenso

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/

2 pensamentos sobre “Biblioteca em C: Números por Extenso

  1. Pingback: Classe Python de Números Por Extenso | Daemonio Labs

Deixe um comentário