Este articulo es una versión actualizada para .Net del script de Perl hecho por Erik Peterson.

En China, si bien muchos han adoptado el sistema de numeración arábigo que es mundialmente reconocido, también se sigue usando el sistema el sistema nativo de caracteres chinos. El sistema tradicional es igualmente un sistema de base 10, pero tiene importantes diferencias en la forma en que son representados los números.(ver tabla) El sistema chino tiene representación para los números del 0 al 9. Adicionalmente se puede representar al cero como un simple circulo. La pronunciación romanizada estándar de los caracteres usados en china se llama “pinyin”. El hànyǔ pīnyīn , literalmente ‘deletreo [fonético] de la lengua de los han (chino mandarín), normalmente llamado pinyin es el sistema de transcripción fonética oficial del chino mandarín (hànyǔ). En lugar de utilizarse los caracteres chinos (que, en general, no dan información acerca de la pronunciación), se usan letras del alfabeto latino para escribir fonéticamente las palabras chinas. Este sistema ayuda a los extranjeros a aprender la pronunciación a través de libros de texto y también es utilizado para introducir texto en caracteres chinos en teclados QWERTY a través de programas específicos para ello.

  0 1 2 3 4 5 6 7 8 9 10 100 1000 10000 100000000
Hanyu Pinyin ling yi er san si wu liu qi ba jiu shi bai quian wan yi

Once en Chino es “diez uno”. Doce es “diez dos”, y así sucesivamente. Veinte seria “Dos diez”, mientras que veintiuno es “dos diez uno” (2*10+1) y así hasta 99. Cien se escribe tal cual. 101 seria “uno cien cero uno”, 111 seria “uno cien uno diez uno”. Note que para representar el once solo es “diez uno” y no “uno diez uno”, pero cuando se usa en un número largo (como 111), se debe agregar un uno extra. Para representar números por arriba de mil es algo similar, donde dices cuantos miles tienes, cuantas centenas, y después decenas y unidades. La excepción de esto es el cero. Cuando el número es un cero (excepto al final), necesitas decir “cero”, pero solo una vez para dos o más ceros consecutivos. Así, por ejemplo, mil y uno (1001) seria “uno mil cero uno”, donde el cero representa al cero en decenas y centenas.

Para la implementación del algoritmo en .Net, primero declararemos dos enumeraciones para manejar los tipos de  salida que podemos manejar, por default manejaremos la representación tradicional, solo si lo solicita el usuario, haremos la conversión a la representación en Pinyin.

public enum OutputType { Traditional, Pinyin };

Ahora declararemos variables constantes que nos ayudarán a contener los caracteres que representan al punto decimal y al signo negativo:

private const string MINUS = “負”;
private const string DECIMAL = “點”;

ahora declareremos el array digits que contiene la representación de cada numero del 0 al 9 en ese orden.

private string[] digits = new string[] { “零”, “一”, “二”, “三”, “四”, “五”, “六”, “七”, “八”, “九” };

Ahora declararemos un array que contiene la representación de cada posición decimal hasta los miles y otro que contiene la representación a partir de mil hasta billones, esto será para cumplir con las reglas gramaticales que vimos anteriormente en la representación antes y después de los miles:

private string[] beforeWan = new string[] { “十”, “百”, “千” };

private string[] afterWan = new string[] { “”, “萬”, “億”, “兆”, “京” };

A continuación emplearemos un diccionario en cual contendrá la equivalencia de cada numeral chino en su representación en pinyin, el cual emplearemos al final si el outputType es igual a pinyin.

private Dictionary<string, string> trad2pinyin = new Dictionary<string, string>
           {{“負” , “fu”},   {“點” , “dian”},  {“零” , “ling”},  {“一” , “yi”},
           {“二” , “er”},  {“三” , “san”},  {“四” , “si”},  {“五” , “wu”}, {“六” , “liu”},
           {“七” , “qi”}, {“八” , “ba”}, {“九” , “jiu”}, {“十” , “shi”}, {“百” , “bai”},
           {“千” , “qian”},  {“萬” , “wan”}, {“億” , “yi”}, {“兆” , “zhao”},  {“兩” , “liang”}};

Deliberadamente he omitido el carácter de tono, lo tendrán que investigar…… ;-)

Ahora declararé el método de conversión, el cual recibe un double y un OutputType

public string NumberToWords(double enumber, OutputType outputType = default_outputtype)
        {

}

Primero verificaremos que el número que nos pasan no es igual a cero, en caso contrario regresaremos el carácter de cero y terminaremos la función. Después verificaremos si es un número negativo, en ese caso habilitaremos una bandera y tomaremos el valor absoluto del numeral.

          if (enumber == 0)
            {
                return digits[0];
            }

            if (enumber < 0)
            {
                negative = 1;
                enumber = enumber * -1;
            }

ahora verificaremos si el número tiene una parte decimal, si la tiene colocaremos el valor del decimal en una variable y el valor entero en otra, después analizaremos el número entero y sacaremos el valor de cada posición decimal dentro del array “powers”, este array puede ser un array fijo o ponemos emplear un ArrayList.

if (enumber.ToString().Contains(“.”))
            {
                remainder = enumber.ToString().Split(‘.’)[1];
                enumber = double.Parse(enumber.ToString().Split(‘.’)[0]);
            }

while (System.Math.Pow(TEN, power) <= enumber)
            {
                value = (enumber % (System.Math.Pow(TEN, (power + 1))) / System.Math.Pow(TEN, power));
                powers[power] = value;
                enumber -= enumber % System.Math.Pow(TEN, (power + 1));
                power++;
            }

Finalmente recorreremos el array “powers” para ir formando la representación de caracteres chinos empleando el array de digits para extraer la representación de cada cifra.

for (int i = 0; i < power; i++)
            {
                if ((i % 4) == 0)
                {
                    if (powers[i] != 0)
                    {
                        inzero = 0;
                        canaddzero = 1;
                        cnumber = string.Format(“{0}{1}{2}”, digits[(int)powers[i]], afterWan[i / 4], cnumber);
                    }
                    else
                    {
                        if (((i + 3 < power) && powers[i + 3] != 0) ||
                                    ((i + 2 < power) && powers[i + 2] != 0) ||
                                    ((i + 1 < power) && powers[i + 1] != 0))
                        {
                            cnumber = string.Format(“{0}{1}”, afterWan[i / 4], cnumber);
                            canaddzero = 0;
                        }
                    }
                }
                else
                {
                    if (powers[i] != 0)
                    {
                        inzero = 0;
                        canaddzero = 1;
                        if (power == 2 && i == 1 && powers[i] == 1)
                        {
                            cnumber = beforeWan[(i % 4) - 1] + cnumber;
                        }
                        else
                        {
                            cnumber = digits[(int)powers[i]] + beforeWan[(i % 4) - 1] + cnumber;
                        }
                    }
                    else
                    {
                        if (canaddzero == 1 && inzero == 0)
                        {
                            inzero = 1;
                            cnumber = digits[(int)powers[i]] + cnumber;
                        }
                    }
                }
            }

Finalmente verificamos la bandera de número negativo y la representación de los números fraccionarios.

if (remainder != “”)
            {
                cnumber += DECIMAL;
                for (int i = 0; i < remainder.Length; i++)
                {
                    cnumber += digits[int.Parse(remainder.Substring(i, 1))];
                }
            }

if (negative == 1)
            {
                cnumber = MINUS + cnumber;
            }

si el OutputType es Pinyin, haremos la conversión empleando el array, de otra forma mandamos la salida al usuario:

string result = string.Empty;

switch (outputType)
           {
               case OutputType.Traditional:
                   result = cnumber;
                   break;

case OutputType.Pinyin:
                    for (int j = 0; j < cnumber.Length; j++)
                    {
                        result += trad2pinyin[cnumber.Substring(j, 1)] + ” “;
                    }
                    break;

}

return result;

Espero que les sea de utilidad esta función, feliz código….. espero sus comentarios.

About these ads