Cargando...
 
Imprimir

Escribir números con letras

Image
Con éste escueto título pretendemos presentar un algoritmo tal que dada una cantidad numérica entera y positiva sea capaz de expresarla con texto, es decir, tal y como la leemos.

Este tipo de algoritmos se utiliza, por ejemplo, en la emisión de cheques bancarios y otros documentos en los que se escriben cifras como método adicional para evitar errores y/o falsificaciones. También se utilizan en aplicaciones de síntesis de voz, en los cuales puede ser necesario "leer" las cifras.

Vamos ahondar un poco más en el tema y a descubrir los problemas a los que nos enfrentamos.

Un poco de gramática y lexicología

En principio, no es un algoritmo complicado, ya que el sistema que utilizamos para leer las cifras está basado en dar nombre a las unidades, decenas y centenas (con lo que podemos leer los nombres de cualquier números de tres cifras) y a partir de ahí utilizar agrupaciones de tres en tres (miles) y de seis en seis (millones, billones, etc...). Es decir, podemos encontrar una estructura regular en la forma de leer los números.

Sin embargo, hacerlo relativamente bien en español no es tan sencillo (comparado con idiomas como el inglés, por ejemplo), ya que por un lado se utiliza una enorme cantidad de irregularidades (por ejemplo, números con nombre propio, como el doce, que si tuviera un nombre regular debería llamarse "diez y dos" y de estos tenemos un montón -del once al veintinueve-) y por otro lado, el hecho de que en español los números pueden actuar tanto de pronombre como de determinante, y en éste último caso, tanto femenino como masculino (Ejemplo: 761 puede actuar como pronombre: "Setecientos sesenta y uno", como determinante masculino: "Setecientos sesenta y un euros", o femenino: "Setecientas sesenta y una libras esterlinas").

Empecemos por el hecho de que una cantidad escrita puede actuar como pronombre, o como determinante. (Aquí me han ayudado. Gracias kitty ;-)

  • Los determinantes son una clase de palabras muy heterogéneas, pero en general, y simplificando mucho, podríamos decir que acompañan siempre a un sustantivo, es decir, a un nombre, haciendo más preciso su significado. En español, cuando los números actúan de determinante tienen género, el mismo del sustantivo al que acompañan. Quizá el determinante más sencillo de este tipo sea el del número 1: Ejemplos: Una jirafa, un león.
    Pero lo mismo ocurre si el número es otro:
    • Doscientas veintiuna jirafas
    • Doscientos veintiún leones
  • Los pronombres son palabras -al igual que antes, simplificando mucho- que pueden tomar el papel de un sustativo. En el caso de los números, representan al propio nombre del número.
    Por ejemplo:
    • ¿En qué año naciste? En mil novecientos cuarenta y uno (Fíjate: ni "cuarenta y un" ni "cuarenta y una")
    • ¿Qué número ha salido premiado en la lotería de ayer?: El treinta y seis mil setecientos ochenta y uno.

El segundo tema son las irregularidades. (Nota: al hablar de la primera, segunda, tercera... cifras de un número empezamos a contar por la derecha, la menos significativa, la unidad. Es decir, la primera es la unidad, la segunda la decena... etc.)

  • Las cifras básicas (unidades) tienen nombre: uno, dos, tres... hasta el nueve. Las decenas también: diez, veinte, treinta... hasta el noventa. La regla general dice que un número de dos cifras se forma con el nombre de la decena, la conjunción "y" y el nombre de la unidad. Por ejemplo, el 38: "treinta y ocho", pero esto no es cierto para los números 11 a 19 y 21 a 29, que tienen nombre propio: once, doce, trece, catorce, quince.... y veintiuno, veintidós, veintitrés... hasta el veintinueve. Pero no sólo eso, en los que terminan en 1, la expresión del número cambia según actúen de determinante masculino, femenino o pronombre. Ejemplo: veintiún, veintiuna, veintiuno. También hay que poner ojo a las tildes: un, dos y tres no llevan tilde, y tampoco treinta y un, treinta y dos y treinta y tres, pero sí llevan veintiún, veintidós y veintitrés.
    (NOTA: Observa que las veintenas empiezan por veinti-, y no venti-... Es decir, se dice veintitrés, y no ventitrés)
  • Las centenas también tienen nombre propio: cien, doscientos, trescientos, cuatrocientos... pero también cambian cuando el número actua de determinante femenino por un lado (ej: trescientas) o de determinante masculino o pronombre por otro (trescientos). El cien es otra excepción... cuando la cifra es exactamete 100 se llama "cien", y si no es así "ciento".
  • A partir de la cuarta cifra hasta la sexta se repite el mismo esquema que de la 1ª a la 3ª. Basta con agregar la partícula "mil"... pero también hay irregularidades. Aquí ya no importa si el número actúa como pronombre. Las cifras 4ª a 6ª forman en sí mismas un número que es determinante del "mil". Pero ese "mil" se interpreta como masculino si toda la cifra está actuando como pronombre o determinante masculino, y como femenino si toda la cifra está actuando como determinante femenino. Ej: 721000: Setecientas veintiuna mil (det. femenino) o 721000: Setecientos veintiún mil (determinante masculino o pronombre).
  • Hay otra excepción más relacionada con las cifras 4ª a 6ª: si la cantidad que representan es exactamente 1, no se menciona, y sólo se pone la partícula "mil", en lugar de "un mil". Ej: 1200 libras: mil doscientas libras (en lugar de una mil doscentas libras). 101200: ciento una mil doscientas libras.
  • A partir de la 7ª cifra y hasta la 12ª podemos repetir el mismo proceso que hacemos con las cifras 1ª a 6ª. De hecho, este esquema se vuelve a repetir cada 6 cifras (de la 13ª a la 18ª, de la 19ª a la 24ª...) Ya sabemos que las cifras 7ª a 12ª representan millones... Pero:
    • Esas cifras actúan como determinante de los millones (y no de todo el número), y por lo tanto, tienen género másculino.
    • Los millones tienen número: singular cuando es exactamente un millón, y plural cuando son dos o más millones.
  • Las siguientes agrupaciones de seis cifras determinan a los billones (cifras 13ª a 18ª), trillones (cifras 19ª a 24ª) y cuatrillones (25ª a 30ª) con ls mismas reglas que para los millones. Más allá de eso, no se suelen leer las cifras.

Los billones, la escala larga y la escala corta.
Una puntualización importante. En aquellos lugares influenciados por el habla inglesa, la palabra "billion" equivale a los "mil millones" en español, y la palabra "trillion" equivale al "billón" en español... (también ocurre algo similar en ruso, griego y brasileño). Es lo que llaman la "escala corta", en la que los grupos de cifras más grandes se hacen de tres en tres en lugar de de seis en seis (la "escala larga" que se utiliza en español y el resto de idiomas).

Hay voces que dicen que eso provoca confusión, y han propuesto la utilización en español de extraños palabros como "millardo" y la escala corta de numeración también en español. Por supuesto que sería fantástico que todos los paises de mundo utilizaramos una forma similar de nombrar a los números... pero de momento no la hay.... cada lengua tiene su propia forma de leer los números basada en la escala corta o en la larga... pero un número es siempre el mismo número, se lea en un idioma o en otro.
Las confusiones que yo he notado siempre han sido por parte de medios de comunicación en los que se traduce una cifra de un idioma a otro literalmente (interpretando "billion" en inglés, como "billón", en español. Así que en este artículo prescindiremos de cualquier otro matiz o discusión al respecto... las cifras en unos idiomas se leen de una manera, y en otros de otra. En español, por el momento, se utiliza la escala larga.


El algoritmo

Pero vamos con el algoritmo. En esta ocasión, vamos a construir una clase en C#, a la que le vamos a colocar un método estático public static String Convierte(String s, Tipo t), siendo Tipo un enumerado de tres elementos: public enum Tipo {Pronombre, DetMasculino, DetFemenino};

Al método Convierte le pasaremos una cadena de hasta 30 carácteres, que contendrá la representación de un número con cifras de "0" a "9", sin puntos ni comas ni espacios ni ningún otro símbolo. Éste método devolverá la representación del número escrito, y dependiendo del segundo parámetro, como determinante masculino o femenino, o como pronombre.

Para ello, vamos a apoyarnos en una serie de métodos privados.

  • El método private static bool EsNumero(String s) simplemente nos dirá si la cadena s está formada exclusivamente por carácteres del "0" al "9".
  • El método private static String ConvierteTodo(int i, Tipo t) tomará la cadena y la irá descomponiendo de seis en seis cifras, empezando por las menos significativas, y se las irá pasando al método Convierte6. Éste devolverá el texto correspondiente a esas seis cifras, y de vuelta en ConvierteTodo, se añadirán las particulas correspondientes a millones, billones, trillones y cuatrillones. Se tiene en cuenta que las primeras seis cifras menos significativas actúan como indica la variable "Tipo t", y que los siguientes grupos de seis son determinantes masculinos de "millones", "billones", etc. Se tiene también en cuenta el número (millón vs. millones).
  • El método private static String Convierte6(int i, Tipo t) se encargará de devolver el nombre correspondiente a un grupo de seis cifras, teniendo en cuenta cómo actúa este grupo, según el parámetro t. El proceso consiste en descomponer el grupo de seis en dos partes de tres cifras, y pasar cada parte al método Convierte3. Entre las dos partes que devuelva Convierte3 se insertará la partícula "mil" teniendo en cuenta las irregularidades comentadas.
  • El método private static String Convierte3(int i, Tipo t) se encargará de devolver el nombre correspondiente a un grupo de tres cifras (centenas-decenas-unidades). Se encarga de las irregularidades de las centenas y se apoya en el método Convierte2 para las decenas y las unidades.
  • El método private static String Convierte2(int i, Tipo t) toma un grupo de dos cifras y devuelve su nombre escrito, teniendo en cuenta los nombres propios de los números 10 a 29. Se apoya en Convierte1.
  • Por último, el método private static String Convierte1(int i, Tipo t) simplemente toma un número de una cifra y lo devuelve escrito.

Poco más... el resto es puramente trivial. Aquí tienes la clase entera, úsala como quieras.

En esta ocasión, el código está tanto en C# como en java. Para simplificar el manejo de los índices de los arrays, hemos sacrificado algunas posiciones de memoria introduciendo cadenas vacías, en aras de una mayor claridad del código.



C#
/*
 * Clase en C# para convertir números a su equivalente escrito.
 * Utilización: pásale al método público "Convierte" una cadena
 * de hasta 30 carácteres con la representación del número en cifras, y la
 * forma en la que debe actuar el número, como Pronombre, o como determinante
 * masculino o femenino, mediante un valor del enumerado Tipo, y te devolverá
 * una cadena con el número como texto.
 * 
 * Ejemplo:
 * Console.WriteLine(NumLetras.Convierte("12312343",NumLetras.Tipo.Pronombre));
 * 
 * Puedes usar, modificar y distribuir libremente éste código, teniendo en cuenta
 * que se distribuye "tal cual", sin garantía ni responsabilidad de ningún tipo.
 * Si te resulta util, se agradece un link a "www.latecladeescape.com".
 * 
 * Éste código se acoge los términos de la licencia descrita a continuación en inglés
 * y su utilización de cualquier tipo supone la aceptación de dichos términos.
 * 
 * **************************************************************************
 * Copyright © 2007 by La tecla de ESCAPE <www.latecladeescape.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ****************************************************************************
 * @version 1.0
 * 
 */
 
using System;
using System.Collections.Generic;
using System.Text;
 
 
    class NumLetras
    {
        public enum Tipo {Pronombre, DetMasculino, DetFemenino};
 
        private static String[] nombresIrregulares ={
        "", "uno", "dos", "tres", "cuatro",
        "cinco", "seis", "siete", "ocho", "nueve",
        "diez", "once", "doce", "trece", "catorce",
        "quince", "dieciséis", "diecisiete", "dieciocho", "diecinueve",
        "veinte", "veintiuno", "veintidós", "veintitrés", "veinticuatro",
        "veinticinco", "veintiséis", "veintisiete", "veintiocho", "veintinueve"};
 
        private static String[] decenas={"","","","treinta", "cuarenta", "cincuenta",
            "sesenta", "setenta", "ochenta", "noventa"};
 
        private static String[] centenas ={"", "", "doscient", "trescient","cuatrocient",
            "quinient","seiscient","setecient","ochocient", "novecient"};
 
        private static String[] nombreMillonesSingular ={ "", "millón", "billón", "trillón", "cuatrillón" };
        private static String[] nombreMillonesPlural ={ "", "millones", "billones", "trillones", "cuatrillones" };
 
        private static String Convierte1(int i, Tipo t)
        {   
            String r;
            if ( (i==1) && (t==Tipo.DetMasculino))
            {
                r="un";
            }
            else if ( (i==1) && (t==Tipo.DetFemenino))
            {
                r="una";
            }
            else
            {
                r=nombresIrregulares[i];
            }
            return r;
        }//Convierte1
 
 
        private static String Convierte2(int i, Tipo t)
        {
            StringBuilder r=new StringBuilder();
            if (i <= 9)
            {
                r.Append(Convierte1(i,t));
            }
            else if (i==21 && t!=Tipo.Pronombre) 
            {
                if (t == Tipo.DetMasculino)
                {
                    r.Append("veintiún");
                }
                else 
                {
                    r.Append("veintiuna");
                }
            }
            else if (i >= 10 && i <= 29)
            {
                r.Append(nombresIrregulares[i]);
            }
            else
            {
                r.Append(decenas[i/10]);
                if (i % 10 != 0)
                {
                    r.Append(" y ");
                    r.Append(Convierte1(i%10, t));
                }
            }
            return r.ToString();
        }//Convierte2
 
        private static String Convierte3(int i, Tipo t) 
        {
            StringBuilder r=new StringBuilder();
            if (i <= 99)
            {
                r.Append(Convierte2(i, t));
            }
            else if (i == 100)
            {
                r.Append("cien");
            }
            else
            {
                if (i >= 101 && i <= 199)
                {
                    r.Append("ciento");
                }
                else
                {
                    r.Append(centenas[i / 100]);
                    r.Append(t == Tipo.DetFemenino ? "as" : "os");
                }
                if (i % 100 > 0)
                {
                    r.Append(" ");
                    r.Append(Convierte2(i % 100, t));
                }
            }
            return r.ToString();
        } //Convierte3
 
        private static String Convierte6(int i, Tipo t)
        {
            StringBuilder r=new StringBuilder();
            int tresPrimeros = i / 1000;
            int tresUltimos = i % 1000;
            if (tresPrimeros==1)
            {
                r.Append("mil");
            }
            else if (tresPrimeros >= 2)
            {
                if (t==Tipo.Pronombre)
                    r.Append(Convierte3(tresPrimeros, Tipo.DetMasculino));
                else
                    r.Append(Convierte3(tresPrimeros, t));
                r.Append(" mil");
            }
 
            if (tresUltimos > 0)
            {
                if (tresPrimeros > 0)
                {
                    r.Append(" ");
                }
                r.Append(Convierte3(tresUltimos,t));
            }
            return r.ToString();
        }
 
        private static bool EsNumero(String s)
        {
            bool resultado = true;
            int contador=0;
            int longitud=s.Length;
            while (resultado && contador<longitud)
            {
                if (s[contador] < '0' || s[contador] > '9')
                {
                    resultado = false;
                }
                contador++;
            }
            return resultado;
        }
 
        private static String ConvierteTodo(String s, Tipo t)
        {
            StringBuilder resultado=new StringBuilder();
            int cuenta = s.Length;
            int cuentamillones = 0;
            while (cuenta > 0)
            {
                int inicio = (cuenta - 6 >= 0) ? (cuenta - 6) : 0;
                int longitud = (6 > cuenta) ? cuenta : 6;
                String stemp = s.Substring(inicio, longitud);
                int i6 = int.Parse(stemp);
                if (cuentamillones > 0 && i6>0)
                {
                    if (resultado.Length > 0)
                    {
                        resultado.Insert(0, " ");
                    }
                    if (i6 > 1)
                    {
                        resultado.Insert(0, nombreMillonesPlural[cuentamillones]); 
                    }
                    else
                    {
                        resultado.Insert(0, nombreMillonesSingular[cuentamillones]);
                    }
                    resultado.Insert(0, " "); 
                }
                resultado.Insert(0, Convierte6(i6, t));
                if (cuentamillones == 0)
                {
                    t = Tipo.DetMasculino;
                }
                cuentamillones++;
                cuenta -= 6;
            };
            return resultado.ToString();
        }
        
        public static String Convierte(String s, Tipo t)
        {
            String resultado="";
            s = s.Trim();
            if (s.Length > 30)
            {
                resultado = "Demasiado grande. Llego hasta 30 cifras";
            }
            else if (!EsNumero(s))
            {
                resultado = "La cadena no está formada sólo por números";
            }
            else
            {
                resultado = ConvierteTodo(s, t);
            }
            return resultado;
        }
 
    }





Ultima edición por vic .
Página última modificacion en Martes 21 de Agosto, 2012 15:57:09 CEST.


Quizá te interese también


¿Dónde estoy?

Estás en La tecla de ESCAPE, un sitio web personal en el que nos gusta hablar de algoritmos, metodología de la programación, personajes de informática, tecnología, ingeniería del software, internet, y cualquier otra tontería que se nos ocurra.
Leer más / Términos de uso (ToS)

Este sitio web usa cookies para su funcionamiento. Navegar por éste sitio supone la aceptación de la política de cookies -
Política de cookies +