domingo, 28 de noviembre de 2010

CONVERTIR NUMEROS ROMANOS A LETRAS…EN C#

El día de ayer como no tenía nada que hacer, me encontraba realizando la librería para obtener el RFC, además del tutorial para implementarla, pero leyendo la documentación del SAT me encontré con 2 obstáculos al momento de querer obtener el RFC para personas morales. El primer obstáculo era que si en la razón social se encuentra un numero (12, 13,500, etc.) para hacer el cálculo de la homonimia hay que convertir el numero a su correspondiente en letras, pero hice un poco de memoria y recordé que para convertir de números a letras ya había hecho una librería meses antes (Convertir Numeros a Letras en C#), en fin problema resuelto, pero el segundo obstáculo era que si también en la razón social había cifras en números romanos de igual forma habría que hacer la conversión, por eso en unos cuantos minutos me puse manos a la obra y por eso explico a continuación la forma como solucione ese inconveniente, cabe recalcar que no es la forma más optima, pero fue lo único que se me ocurrió en poco tiempo.

Requisitos:
  • Microsoft Visual Studio 2008
  • Código de la librería para convertir de NumerosaLetras (se incluye en el código fuente que esta al final)
  • Unos cuantos números romanos, J jejejeje…..

Iniciando:
Lo primero que tenemos que hacer es crear un nuevo proyecto del tipo “Aplicación de Consola”, claro, en una nueva solución, después agregar a la solución un nuevo proyecto del tipo “Biblioteca de Clases”, la estructura de la solución deberá quedar más o menos así:

En el proyecto “ConvierteRomanosLetras”, renombramos la clase creada (click derecho sobre el archivo->cambiar nombre), en este caso la nombré “ConvierteRomLetras”.
La clase “ConvierteRomLetras” deberá quedar con el código siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConvierteRomanosaLetras
{
    /// <summary>
    /// Clase para obtener en cifras una cantidad dada en romanos
    /// </summary>
    public class ConvierteRomLetras
    {
        string[,] simbolosromanos =
        {
            {"I","1"},
            {"V","5"},
            {"X","10"},
            {"L","50"},
            {"C","100"},
            {"D","500"},
            {"M","1000"}
        };

        string[,] antecesores =
        {
            {"IV","4"},
            {"IX","9"},
            {"XL","40"},
            {"XC","90"},
            {"CD","400"},
            {"CM","900"}
        };
        //"MCMXCV"

        /// <summary>
        /// Retorno una cifra pero en numeros reales
        /// ejemplo:XX retorna 20
        /// </summary>
        /// <param name="romanos">Cifra en numeros romanos</param>
        /// <returns>El equivalente en numeros reales</returns>
        public string Convierte(string romanos)
        {

            //string cadena = romanos;
            int contador = 0;
            int apuntador=0;
            int suma = 0;

           
        //antecesores
           
        while (apuntador != antecesores.Length/2)
            {
              if (romanos.Contains(antecesores[apuntador, 0]))
                {
                suma += int.Parse(antecesores[apuntador, 1].ToString());
                romanos = romanos.Replace(antecesores[apuntador, 0], "");
                       
                }
                apuntador++;
             }
          
            contador = 0;
            apuntador = 0;

            //sumo uno por uno
            if (romanos.Length > 0)
            {
                while (contador != romanos.Length)
                {
                    while (apuntador != simbolosromanos.Length / 2)
                    {
          
                   if (romanos.Substring(contador, 1) ==  simbolosromanos[apuntador, 0].ToString())
                        {
                            suma += int.Parse(simbolosromanos[apuntador, 1].ToString());
                        }
                        apuntador++;
                    }
                    apuntador = 0;
                    contador++;
                }
            }
            //sumo uno por uno

            return suma.ToString() ;
        }
    }
}

Lo primero que se hace es declarar dos matrices, la primera contiene los simbolos utilizados con su respectivo valor numérico, la segunda matriz contiene los simbolos romanos a los cuales nombre “antecesores” que serian los valores menores a los de la matriz 1.
A continuacion se explica la primera parte del codigo:
//antecesores
while (apuntador != antecesores.Length/2)
 {

   if (romanos.Contains(antecesores[apuntador, 0]))
      {
        suma += int.Parse(antecesores[apuntador, 1].ToString());
        romanos = romanos.Replace(antecesores[apuntador, 0], "");
        
      }
   apuntador++;
 }

Recorro la cadena de los numeros romanos en busca de simbolos que este en la matriz 2, por ejemplo si el numero a convertir es XIX, primero extraigo los que se componen de 2 digitos (IX)  en base a su valor los voy sumando en la variable “suma”, y asi sucesivamente.

Una vez que he depurado la cadena de numeros romanos, me quedaria ir sumandolos de 1 en uno, en base a los valores de la matriz 1
//sumo uno por uno
            if (romanos.Length > 0)
            {
                while (contador != romanos.Length)
                {
                    while (apuntador != simbolosromanos.Length / 2)
                    {
          
                   if (romanos.Substring(contador, 1) ==  simbolosromanos[apuntador, 0].ToString())
                        {
                            suma += int.Parse(simbolosromanos[apuntador, 1].ToString());
                        }
                        apuntador++;
                    }
                    apuntador = 0;
                    contador++;
                }
            }
            //sumo uno por uno
            return suma.ToString() ;


Con esto obtengo el equivalente de los simbolos romanos en numeros, por ejemplo si el romano=XIX en la variable suma tendre almacenado el valor “19”, por ultimo retorno el valor de suma pero como tipo cadena.

Solo nos queda por convertirlo a letras, pero como recordaran ya hemos creado una librería que convierte de numeros a letras (Convertir Numeros a Letras en C#), así que lo unico que tenemos que hacer es agregar la clase al proyecto “ConvierteRomanosaLetras”,

Si hacemos memoria la libreria “ConvierteNumLetras”, le pasamos un numero de tipo Double y nos retorna su valor en letras pero en el formato “LETRASNUMERO DECIMALES/100 PESOS”, para este ejemplo no nos serviria, asi que le quitamos o comentamos el siguiente fragmento de código:
  //string[] decim = Math.Round(cantidad, 2).ToString().Split(new char[] { '.' });//corregido redondear a dos decimales
            //string decimales = "00/100 M.N";
            //if (decim.Length > 1)
            //{
            //    decimales = decim[1].PadRight(2, '0') + "/100 M.N";
            //}
Si no mal recuerdo es de la linea 79-84.

Y casi al final, agregamos el siguiente fragmento de código:
//lineas anexadas
            if (letras.Substring(letras.Length - 1,1).Equals("N"))
            {
                letras += "O";//termina en N, entonces agrego O para que quede ...NO"
            }

con este fragmento lo que hacemos es que anteriormente si le introduciamos un 1 nos retornaba “UN”, pero para nuestro caso deberá retornar “UNO”.

Por ultimo comentar la siguiente parte del codigo:
   return letras.Trim();// + unidad + decimales;

ya que los decimales, ni la unidad “PESOS” nos interesan…

Probando Nuestro Ejemplo:
En el proyecto de aplicación de consola “NumRomanosaLetras”, anexamos el siguiente código a la clase principal, nos deberá quedar de la siguiente manera:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NumRomanosaLetras
{
    class Program
    {
        static void Main(string[] args)
        {
            ConvierteRomanosaLetras.ConvierteRomLetras rom = new ConvierteRomanosaLetras.ConvierteRomLetras();
            ConvierteRomanosaLetras.ConvierteNumLetras letras=new ConvierteRomanosaLetras.ConvierteNumLetras();
            Console.WriteLine("Escriba el numero romano que desea convertir:\n");
            string romano = Console.ReadLine().ToString().ToUpper();
            //RFC.ConvierteNumLetras letras = new RFC.ConvierteNumLetras();
            Console.WriteLine("\nEl número romano '{0}' convertido a numeros reales={1}\nSi lo convertimos a letras={2}",romano,rom.Convierte(romano),letras.ConvierteaLetras(double.Parse(rom.Convierte(romano))));
            Console.WriteLine("\n\nPresione una tecla para salir...");
            Console.ReadLine();
        }
    }
}

A continuacion explico algunas lineas:
Creo una instancia de la clase que convierte romanos a numeros y luego de la clase que convierte de numeros a letras.
ConvierteRomanosaLetras.ConvierteRomLetras rom = new ConvierteRomanosaLetras.ConvierteRomLetras();
            ConvierteRomanosaLetras.ConvierteNumLetras letras=new ConvierteRomanosaLetras.ConvierteNumLetras();

Luego  le indico al usuario que ingrese la cantidad en romanos que desea convertir, el valor introducido lo asigno a la variable “romano” y si las letras introducidas estan en minusculas las transformo a mayusculas.
Console.WriteLine("Escriba el numero romano que desea convertir:\n");
            string romano = Console.ReadLine().ToString().ToUpper();


Muestro el resultado, de la conversion, en la pantalla:
Console.WriteLine("\nEl número romano '{0}' convertido a numeros reales={1}\nSi lo convertimos a letras={2}",romano,rom.Convierte(romano),letras.ConvierteaLetras(double.Parse(rom.Convierte(romano))));
Console.WriteLine("\n\nPresione una tecla para salir...");
Console.ReadLine();


Veamos un ejemplo


Y pues creo que eso es todo…

En esta ocasión no probe del todo el codigo, solo probe con cifras como V,I,IV,MMCMXCIX,Y OTRAS MAS, asi que si encuentran algun error ya saben a donde reportarlo

El código fuente lo pueden descargar de aqui

1 comentario:

  1. Desastroso, no contempla el orden del los digitos a MIL lo traduce como 1051, lamentable!

    ResponderEliminar