Para os preguiçosos:

O projeto de que trato aqui é um conversor de números para sua representação “por extenso”. Se você chegou aqui e não quer ter o trabalho de ler, apenas achar o código, siga este link: código (ou acesso o projeto no google code). Se, por outro lado, estiver em busca de explicações para fazer o seu próprio conversor ou curioso sobre como este conversor aí em cima funciona, leia o resto deste post.

Ábaco

Para os curiosos:

O título é esquisito, mas a dúvida era essa. O problema surgiu quando, num dos relatórios do sistema, o cliente exigiu que os valores fossem escritos de forma numérica (fácil, o NumberFormat dá conta que é uma beleza) seguidos do valor por extenso. Pensei com meu botões: não tem problema, TODO MUNDO já precisou disso um dia, deve ter DÚZIAS de conversores para números por extenso vagando pelo labirinto de Falken. Basta escolher o mais bonitim e boas!

Na verdade a tarefa nem era minha. Caiu pra mim quando surgiu o primeiro bug. A classe que tinham usado funcionava bem, contanto que o número fosse redondo ou pequeno. “Um milhão de reais” ele disse quando pedi pra converter 1.000.000,00. Mas logo depois me cospe “Um milhão e três de reais” pra 1.000.003,00. Hein? Mas peraí, isso eu conserto, vá… Só que o próximo número piorou: “Um milhão e seiscentos e cinquenta mil e novecentos e dez reais”. Pera lá! Tem E demais aí!.

A primeira coisa que pensei foi em achar outro conversor. Doce ilusão. Existir até existem. Vários (ou melhor, várias cópias do mesmo, já que parecem ser todos baseados no mesmo código original). Mas nenhum funciona.

Quer dizer, funcionam… Se você usar os números de teste que o autor usou! (Quem gosta disso pode fazer disto um “caso” para test-driven design, ou o que valha). Saindo um pouco do que o autor tinha em mente, a coisa desanda.

Nesse ponto eu não tinha mais escolha: teria de implementar o meu próprio conversor. Bom, que os outros estavam errados eu sabia, mas e qual a forma correta de se escrever por extenso? Depois de muita discussão e nenhum acordo, decidimos: Vamos consultar uma gramática!Cheque

Na biblioteca, pegamos logo 3 gramáticas. Nenhuma, claro, concordava com a quantidade de “e” que o sujeito usou. Alias, nenhuma delas concordava entre si. Uma, a mais antiga, pregava o uso de vírgulas a torto e a direito:

um bilhão, cento e vinte milhões, duzentos e três mil, cento e quarenta e sete.

A outra era categórica: depois de mil não! Só depois dos “ãos” é que tem vírgula:

um bilhão, cento e vinte milhões, duzentos e três mil cento e quarenta e sete.

E por último, uma que se dizia de acordo com o novo acordo ortográfico (que não tratou nem de números nem de vírgulas, mas que de alguma forma afetava a opinião do autor a respeito) abominava vírgulas:

um bilhão cento e vinte milhões duzentos e três mil cento e quarenta e sete.

Assim fica difícil, né? Em nosso auxílio veio uma ótima e hiper didática revisora de texto que passava pelo local:

Vírgulas servem para separar os elementos numa lista. No caso dos números, use para separar cada grupo de unidade, milhar, milhão, etc. (nesse ponto a gente, das exatas, anota: uma vírgula a cada potência de 1000). Mas elas não são absolutas, e podem ser omitidas se for para aumentar a clareza. Pense na clareza do texto e escolha a forma que melhor lhe convém. Depois de escolhido o melhor jeito, procure uma gramática que concorde com você e use ela como bibliografia!

Pra mim, esse conselho foi genial. E de certa forma resolveu nosso problema: bastava escolher o mais fácil de implementar e depois procurar uma gramática que nos apoiasse.

Acabei me guiando por 8 regrinhas, que definiram como implementar meu conversor:

  1. números abaixo de 20 tem nome próprio;
  2. de 21 a 99 os números são formados por DEZENA “e” UNIDADE (exemplo: “trinta e cinco”);
  3. dezenas redondas não tem nada depois (20 -> “vinte”, e não “vinte e zero”);
  4. 100 tem nome próprio: cem;
  5. números maiores que 100 são compostos por CENTENA “e” DEZENA ["e" UNIDADE];
  6. acima de 1000 agrupa-se os números em blocos de 3 dígitos (potências de 1000), que são representados como se fossem números menores do que 1000 acrescidos do sufixo representando a potência de mil apropriada (mil, milhão, etc);
    1. os grupos são concatenados por vírgula;
    2. A ultima concatenação é feita por “e” (“um milhão e 200 mil”);
    3. A ultima concatenação é omitida (ou substituída por vírgula) caso o ultimo grupo seja maior que 100 e não seja múltiplo e 100 (“mil[,] duzentos e cinquenta”)
  7. o “um” em frente ao descritor de grupo “mil” é opcional e deve ser parametrizável (“mil e um” e “um mil e um” são igualmente aceitáveis);
  8. Ao acrescentar a unidade (por exemplo “reais”) usa-se o prefixo “de” antes da unidade caso o último sufixo seja de milhão ou maior (“dez milhões de reais”, mas “dez mil reais”).

Código

Com essas regras a implementação foi quase direta. Primeiro criei uma função para as unidades:

        String unidades(int n) {
                return UNIDADES[n];
        }

Em seguida as dezenas:

        String dezenas(int n) {
                if (n < UNIDADES.length) return unidades(n);
                int unidade = n % 10;
                n = n / 10;
                return DEZENAS[n] + " e " + unidades(unidade);
        }

depois tratei a exceção da regra 3:

        String dezenas(int n) {
                . . .
                String unidadeStr = "";
                if (unidade != 0) {
                        unidadeStr = " e " + unidades(unidade);
                }
                return DEZENAS[n] + unidadeStr;
        }

E seguindo as regras uma a uma cheguei no resultado final, que pode ser visto no google code. Ainda não está perfeito, e ainda quero brincar bastante com esse conversor, então deixem suas sugestões,seja aqui, seja como “bug” no google code, que eu implemento, caso dê tempo (e ânimo).