Indefectiblemente en algún momento necesitamos o vamos a necesitar código para transformar un número a su numeral cardinal, o sea: 44 en “Cuarenta y cuatro”. Este es otro caso en donde buscamos en google “numeros a letras” o algo así esperando una solución hecha. Bueno, aca la mía: Bajar Numalet, Esta es una solución de VS 2005 con una clase en C#, en VB, un form para probar la clase y un sencillo test unitario para comprobar que las clases en ambos lenguajes funcionan igual.
Al final del post está el código de la clase en C# y en VB para copiarlo directamente.
¿por que usar esta clase y no otra? realmente no hay ningún motivo en especial, cualquier código que resuelva este trivial tema esta bien. La clase que presento genera números acorde a la especificación de la RAE, respetando acentos, apócopes, etc.
Por otro lado, todos los algoritmos que vi generan la salida según el uso del país de quien lo programa, y si queremos otra salida debemos copiar el algoritmo o perder la configuración anterior, asique agregué algunas propiedades que permiten configurar la salida con bastante libertad para cada instancia de la clase.
Hechas estas aclaraciones, veamos un ejemplo sencillo:
C#: Numalet.ToCardinal(8,25);
VB: Numalet.ToCardinal(8,25)
retorna:
ocho con 25/100.-
No hace falta instanciar la clase ya que el método ToCardinal es estático, ahora, si queremos una salida distinta (ahora si instanciando la clase), podemos jugar con las siguientes propiedades:
- CultureInfo: Permite especificar que configuración regional se utiliza para parsear los números en la sobrecarga que toma strings, en nuestro caso nos importa qué separador decimal (punto o coma) reconocerá, por defecto está seteado en la configuración del thread en que corre el proceso, para cambiarlo de manera que reconozca el punto, por caso, el valor de la propiedad podría ser new CultureInfo(«en-US»). El método estático ToCardinal tiene opcionalmente una sobrecarga que acepta un CultureInfo, por lo que en una máquina donde el separador decimal es la coma, podemos traducir un número que tiene punto como separador decimal de la siguiente manera:
Numalet.ToCardinal(«15.2»,CultureInfo(”en-US”));
Lo que retorna: «quince con 20/100.-«.
- SeparadorDecimalSalida: Es el texto que aparece entre la parte entera y la decimal, por defecto “con”, por ejemplo: 2,28 = Dos con 28/100.-
- LetraCapital: Por defecto false, permite obtener “quince” o “Quince“.
- ConvertirDecimales: Por defecto false, permite especificar si los decimales se muestran como número o como texto.
- MascaraSalidaDecimal: Es la forma en la que se muestran los decimales, utiliza una cadena para dar formato, por defecto es “00/100.-”.
El comportamiento varía según se estén convirtiendo o no los decimales. Para ello he implementado la siguiente lógica: Si el valor de MascaraSalidaDecimal comienza con ‘#’ o ‘0’, se interpreta como una máscara ‘numérica’ y se utiliza como máscara para los decimales, por ejemplo el caso por defecto (00/100.-).
Si en cambio la cadena empieza por cualquier otro caracter, se toma como literal y se agrega al final del resultado. Por ejemplo: MascaraSalidaDecimal = «centavos» => 45,28 = cuarenta y cinco con 28 centavos.
Hay que notar que todos los caracteres posteriores a los ‘0’ o ‘#’ se toman como literales, internamente se entrecomillan con (‘ ) comilla simple, por lo tanto, si queremos que aparezca una comilla simple en la máscara, debemos escribirla doble, por ej. si MascaraSalidaDecimal = litros de Jack Daniel»s, (y SeparadorDecimalSalida = ‘coma’ ) entonces 1,2 = uno coma dos litros de Jack Daniel’s.
- Decimales: Indica la posición decimal que se utilizará para redondear los decimales al convertirlos a enteros. Recordemos que al pasar un número a su nombre, los decimales se expresan como un entero (Ej. 0,21 = ‘cero con 21 centésimas’, el 21 es entero), pero en un número como 8,54613654 ¿cual será el entero que represente los decimales?. Bueno, es lo que se especifica con esta propiedad, por ejemplo si Decimales=2 (valor por defecto) entonces 8,54613654 = ‘ocho con 55/100.-‘
Esta propiedad cambia automáticamente al variar MascaraSalidaDecimal. La cantidad de caracteres ‘0’ o ‘#’ hasta el primer caracter distinto se toma como valor para Decimales. O sea, si MascaraSalidaDecimal = «##0 sobre mil», entonces Decimales = 3, por lo que 456,45667 se convierte en ‘cuatrocientos cincuenta y seis con 457 sobre mil’
Tener en cuenta que el funcionamiento de MascaraSalidaDecimal es un poco diferente que las cadenas de formato del framework, por ejemplo, es lo mismo en el framework hacer #/100 que ###/100, pero en nuestro caso sirve para establecer que queremos redondear en tres decimales.
Sea cual sea el valor de MascaraSalidaDecimal podemos cambiar la propiedad Decimales con posterioridad y a nuestro gusto.
- ApocoparUnoParteDecimal: En castellano, cuando los números cuantifican un sustantivo masculino, se apocopa (se recorta el final de la palabra) de las decenas mayores a veinte terminadas en uno, ¿que? que en vez de poner “treinta y uno elefantes” ponemos “treinta y un elefantes”. Esta propiedad se puede setear individualmente pero también cambia junto con el valor de ConvertirDecimales ya que si queremos convertir decimales a texto, lo normal es apocopar la palabra uno, por ejemplo veintiún centavos. Tener en cuenta que se utiliza solo cuando ConvertirDecimales es verdadero.
- ApocoparUnoParteEntera: Lo mismo que la anterior pero para las unidades de nuestro número. Esta propiedad cambia junto con SeparadorDecimalSalida. Si seteamos esta última a, por ejemplo, «pesos con», entonces se deduce que estoy cualificando algo (pesos) y la segunda palabra (con) se toma como conjunción (adverbio conjuntivo para los puristas creo), por lo que ApocoparUnoParteEntera pasa a ser true. ¿No entendiste? somos dos, veamos algunos ejemplos:
- si SeparadorDecimalSalida = ‘con’ entonces ApocoparUnoParteEntera es false y 31,2 = ‘treinta y uno con 20/100.-‘
- si SeparadorDecimalSalida = ‘euros con’ entonces ApocoparUnoParteEntera es true y 31,2 = ‘treinta y un euros con 20/100.-‘
Esta propiedad también se puede cambiar con posterioridad a SeparadorDecimalSalida si no se desea este comportamiento.
El código tiene en cuenta que:
- Los números 16,21,22,23 y 26 se acentúan.
- El 21 cuando se apocopa se acentúa (veintiún).
- Cuando cuantificamos miles, cientos de miles o millones, hay que apocopar la palabra o terminación “uno”, por ejemplo 21 es “veintiuno”, pero 21.000 es “veintiún mil”.
- Permite convertir números mayores o iguales a cero y menores que un billón. o sea, el mayor número permitido es 999.999.999.999 ¿te alcanza?
No tiene en cuenta el género femenino, por ejemplo no genera “quinientas mujeres”. Se puede tocar el código para tener en cuenta esto, pero hay que tener cuidado porque los millones son siempre masculinos: “quinientos millones quinientas mil mujeres”, y los apocopés no se utilizan en numéros femeninos, por ej. ‘treinta y una montañas’ y no ‘treinta y un montañas’. Si alguien lo utiliza lo agrego.
Rendimiento:
La clase convierte 1.000.000 de números generados al azar en menos de 7 segundos en mi máquina (P4). Casi no hay ventaja en instanciar la clase, conviene hacerlo solo si vamos a variar la configuración.
Algunos ejemplos:
MessageBox.Show(Numalet.ToCardinal("18,25")); //dieciocho con 25/100.- //Si tenemos el número en un string con otro separador decimal (por ejemplo punto): MessageBox.Show(Numalet.ToCardinal("155.38", new CultureInfo("en-US"))); //ciento cincuenta y cinco con 38/100.- //instanciando la clase podemos generar salidas variadas Numalet let; let = new Numalet(); //un porcentaje como he visto en algunos documentos de caracter legal: let.MascaraSalidaDecimal = "por ciento"; let.SeparadorDecimalSalida = "con"; let.ConvertirDecimales = true; MessageBox.Show(let.ToCustomCardinal(21.2)); //veintiuno con veinte por ciento let = null; let = new Numalet(); //al uso en México (creo): let.MascaraSalidaDecimal = "00/100 M.N."; let.SeparadorDecimalSalida = "pesos"; //observar que sin esta propiedad queda "veintiuno pesos" en vez de "veintiún pesos": let.ApocoparUnoParteEntera = true; MessageBox.Show("Son: " + let.ToCustomCardinal(1121.24)); //Son: un mil ciento veintiún pesos 24/100 M.N. //algo más raro let.MascaraSalidaDecimal = "###0 dracmas"; //###0 quiere hace que se redondee a 4 decimales y no se muestren los ceros a la izquierda, //en cambio 0000 haría que, aparte de redondear en 4, se muestren los ceros a la izquierda. let.SeparadorDecimalSalida = "talentos y"; let.LetraCapital = true; MessageBox.Show(let.ToCustomCardinal(12.085)); //Doce talentos y 850 dracmas //una variación del anterior redondeando decimales a mano let.ConvertirDecimales = true; //redondeando en cuatro decimales let.Decimales = 4; let.MascaraSalidaDecimal = "dracmas"; MessageBox.Show(let.ToCustomCardinal(21.50028354)); //Veintiún talentos y cinco mil tres dracmas let = null;
Para quien quiera un comportamiendo fijo distinto al que tiene la clase, puede cambiar los valores de las constantes en la clase.
Espero que les sirva y saludos.
Clase en C#:
using System; using System.Text; using System.Globalization; /// <summary> /// Convierte números en su expresión numérica a su numeral cardinal /// </summary> public sealed class Numalet { #region Miembros estáticos private const int UNI = 0, DIECI = 1, DECENA = 2, CENTENA = 3; private static string[,] _matriz = new string[CENTENA + 1, 10] { {null," uno", " dos", " tres", " cuatro", " cinco", " seis", " siete", " ocho", " nueve"}, {" diez"," once"," doce"," trece"," catorce"," quince"," dieciséis"," diecisiete"," dieciocho"," diecinueve"}, {null,null,null," treinta"," cuarenta"," cincuenta"," sesenta"," setenta"," ochenta"," noventa"}, {null,null,null,null,null," quinientos",null," setecientos",null," novecientos"} }; private const Char sub = (Char)26; //Cambiar acá si se quiere otro comportamiento en los métodos de clase public const String SeparadorDecimalSalidaDefault = "con"; public const String MascaraSalidaDecimalDefault = "00'/100.-'"; public const Int32 DecimalesDefault = 2; public const Boolean LetraCapitalDefault = false; public const Boolean ConvertirDecimalesDefault = false; public const Boolean ApocoparUnoParteEnteraDefault = false; public const Boolean ApocoparUnoParteDecimalDefault = false; #endregion #region Propiedades private Int32 _decimales = DecimalesDefault; private CultureInfo _cultureInfo = CultureInfo.CurrentCulture; private String _separadorDecimalSalida = SeparadorDecimalSalidaDefault; private Int32 _posiciones = DecimalesDefault; private String _mascaraSalidaDecimal, _mascaraSalidaDecimalInterna = MascaraSalidaDecimalDefault; private Boolean _esMascaraNumerica = true; private Boolean _letraCapital = LetraCapitalDefault; private Boolean _convertirDecimales = ConvertirDecimalesDefault; private Boolean _apocoparUnoParteEntera = false; private Boolean _apocoparUnoParteDecimal; /// <summary> /// Indica la cantidad de decimales que se pasarán a entero para la conversión /// </summary> /// <remarks>Esta propiedad cambia al cambiar MascaraDecimal por un valor que empieze con '0'</remarks> public Int32 Decimales { get { return _decimales; } set { if (value > 10) throw new ArgumentException(value.ToString() + " excede el número máximo de decimales admitidos, solo se admiten hasta 10."); _decimales = value; } } /// <summary> /// Objeto CultureInfo utilizado para convertir las cadenas de entrada en números /// </summary> public CultureInfo CultureInfo { get { return _cultureInfo; } set { _cultureInfo = value; } } /// <summary> /// Indica la cadena a intercalar entre la parte entera y la decimal del número /// </summary> public String SeparadorDecimalSalida { get { return _separadorDecimalSalida; } set { _separadorDecimalSalida = value; //Si el separador decimal es compuesto, infiero que estoy cuantificando algo, //por lo que apocopo el "uno" convirtiéndolo en "un" if (value.Trim().IndexOf(" ") > 0) _apocoparUnoParteEntera = true; else _apocoparUnoParteEntera = false; } } /// <summary> /// Indica el formato que se le dara a la parte decimal del número /// </summary> public String MascaraSalidaDecimal { get { if (!String.IsNullOrEmpty(_mascaraSalidaDecimal)) return _mascaraSalidaDecimal; else return ""; } set { //determino la cantidad de cifras a redondear a partir de la cantidad de '0' o '#' //que haya al principio de la cadena, y también si es una máscara numérica int i = 0; while (i < value.Length && (value[i] == '0') | value[i] == '#') i++; _posiciones = i; if (i > 0) { _decimales = i; _esMascaraNumerica = true; } else _esMascaraNumerica = false; _mascaraSalidaDecimal = value; if (_esMascaraNumerica) _mascaraSalidaDecimalInterna = value.Substring(0, _posiciones) + "'" + value.Substring(_posiciones) .Replace("''", sub.ToString()) .Replace("'", String.Empty) .Replace(sub.ToString(), "'") + "'"; else _mascaraSalidaDecimalInterna = value .Replace("''", sub.ToString()) .Replace("'", String.Empty) .Replace(sub.ToString(), "'"); } } /// <summary> /// Indica si la primera letra del resultado debe estár en mayúscula /// </summary> public Boolean LetraCapital { get { return _letraCapital; } set { _letraCapital = value; } } /// <summary> /// Indica si se deben convertir los decimales a su expresión nominal /// </summary> public Boolean ConvertirDecimales { get { return _convertirDecimales; } set { _convertirDecimales = value; _apocoparUnoParteDecimal = value; if (value) {// Si la máscara es la default, la borro if (_mascaraSalidaDecimal == MascaraSalidaDecimalDefault) MascaraSalidaDecimal = ""; } else if (String.IsNullOrEmpty(_mascaraSalidaDecimal)) //Si no hay máscara dejo la default MascaraSalidaDecimal = MascaraSalidaDecimalDefault; } } /// <summary> /// Indica si de debe cambiar "uno" por "un" en las unidades. /// </summary> public Boolean ApocoparUnoParteEntera { get { return _apocoparUnoParteEntera; } set { _apocoparUnoParteEntera = value; } } /// <summary> /// Determina si se debe apococopar el "uno" en la parte decimal /// </summary> /// <remarks>El valor de esta propiedad cambia al setear ConvertirDecimales</remarks> public Boolean ApocoparUnoParteDecimal { get { return _apocoparUnoParteDecimal; } set { _apocoparUnoParteDecimal = value; } } #endregion #region Constructores public Numalet() { MascaraSalidaDecimal = MascaraSalidaDecimalDefault; SeparadorDecimalSalida = SeparadorDecimalSalidaDefault; LetraCapital = LetraCapitalDefault; ConvertirDecimales = _convertirDecimales; } public Numalet(Boolean ConvertirDecimales, String MascaraSalidaDecimal, String SeparadorDecimalSalida, Boolean LetraCapital) { if (!String.IsNullOrEmpty(MascaraSalidaDecimal)) this.MascaraSalidaDecimal = MascaraSalidaDecimal; if (!String.IsNullOrEmpty(SeparadorDecimalSalida)) _separadorDecimalSalida = SeparadorDecimalSalida; _letraCapital = LetraCapital; _convertirDecimales = ConvertirDecimales; } #endregion #region Conversores de instancia public String ToCustomCardinal(Double Numero) { return Convertir((Decimal)Numero, _decimales, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _convertirDecimales, _apocoparUnoParteEntera, _apocoparUnoParteDecimal); } public String ToCustomCardinal(String Numero) { Double dNumero; if (Double.TryParse(Numero, NumberStyles.Float, _cultureInfo, out dNumero)) return ToCustomCardinal(dNumero); else throw new ArgumentException("'" + Numero + "' no es un número válido."); } public String ToCustomCardinal(Decimal Numero) { return ToCardinal((Numero)); } public String ToCustomCardinal(Int32 Numero) { return Convertir((Decimal)Numero, 0, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _convertirDecimales, _apocoparUnoParteEntera, false); } #endregion #region Conversores estáticos public static String ToCardinal(Int32 Numero) { return Convertir((Decimal)Numero, 0, null, null, true, LetraCapitalDefault, ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault); } public static String ToCardinal(Double Numero) { return ToCardinal((Decimal)Numero); } public static String ToCardinal(String Numero, CultureInfo ReferenciaCultural) { Double dNumero; if (Double.TryParse(Numero, NumberStyles.Float, ReferenciaCultural, out dNumero)) return ToCardinal(dNumero); else throw new ArgumentException("'" + Numero + "' no es un número válido."); } public static String ToCardinal(String Numero) { return Numalet.ToCardinal(Numero, CultureInfo.CurrentCulture); } public static String ToCardinal(Decimal Numero) { return Convertir(Numero, DecimalesDefault, SeparadorDecimalSalidaDefault, MascaraSalidaDecimalDefault, true, LetraCapitalDefault, ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault); } #endregion private static String Convertir(Decimal Numero, Int32 Decimales, String SeparadorDecimalSalida, String MascaraSalidaDecimal, Boolean EsMascaraNumerica, Boolean LetraCapital, Boolean ConvertirDecimales, Boolean ApocoparUnoParteEntera, Boolean ApocoparUnoParteDecimal) { Int64 Num; Int32 terna, centenaTerna, decenaTerna, unidadTerna, iTerna; String cadTerna; StringBuilder Resultado = new StringBuilder(); Num = (Int64)Math.Abs(Numero); if (Num >= 1000000000000 || Num < 0) throw new ArgumentException("El número '" + Numero.ToString() + "' excedió los límites del conversor: [0;1.000.000.000.000)"); if (Num == 0) Resultado.Append(" cero"); else { iTerna = 0; while (Num > 0) { iTerna++; cadTerna = String.Empty; terna = (Int32)(Num % 1000); centenaTerna = (Int32)(terna / 100); decenaTerna = terna % 100; unidadTerna = terna % 10; if ((decenaTerna > 0) && (decenaTerna < 10)) cadTerna = _matriz[UNI, unidadTerna] + cadTerna; else if ((decenaTerna >= 10) && (decenaTerna < 20)) cadTerna = cadTerna + _matriz[DIECI, unidadTerna]; else if (decenaTerna == 20) cadTerna = cadTerna + " veinte"; else if ((decenaTerna > 20) && (decenaTerna < 30)) cadTerna = " veinti" + _matriz[UNI, unidadTerna].Substring(1); else if ((decenaTerna >= 30) && (decenaTerna < 100)) if (unidadTerna != 0) cadTerna = _matriz[DECENA, (Int32)(decenaTerna / 10)] + " y" + _matriz[UNI, unidadTerna] + cadTerna; else cadTerna += _matriz[DECENA, (Int32)(decenaTerna / 10)]; switch (centenaTerna) { case 1: if (decenaTerna > 0) cadTerna = " ciento" + cadTerna; else cadTerna = " cien" + cadTerna; break; case 5: case 7: case 9: cadTerna = _matriz[CENTENA, (Int32)(terna / 100)] + cadTerna; break; default: if ((Int32)(terna / 100) > 1) cadTerna = _matriz[UNI, (Int32)(terna / 100)] + "cientos" + cadTerna; break; } //Reemplazo el 'uno' por 'un' si no es en las únidades o si se solicító apocopar if ((iTerna > 1 | ApocoparUnoParteEntera) && decenaTerna == 21) cadTerna = cadTerna.Replace("veintiuno", "veintiún"); else if ((iTerna > 1 | ApocoparUnoParteEntera) && unidadTerna == 1 && decenaTerna != 11) cadTerna = cadTerna.Substring(0, cadTerna.Length - 1); //Acentúo 'veintidós', 'veintitrés' y 'veintiséis' else if (decenaTerna == 22) cadTerna = cadTerna.Replace("veintidos", "veintidós"); else if (decenaTerna == 23) cadTerna = cadTerna.Replace("veintitres", "veintitrés"); else if (decenaTerna == 26) cadTerna = cadTerna.Replace("veintiseis", "veintiséis"); //Completo miles y millones switch (iTerna) { case 3: if (Numero < 2000000) cadTerna += " millón"; else cadTerna += " millones"; break; case 2: case 4: if (terna > 0) cadTerna += " mil"; break; } Resultado.Insert(0, cadTerna); Num = (Int32)(Num / 1000); } //while } //Se agregan los decimales si corresponde if (Decimales > 0) { Resultado.Append(" " + SeparadorDecimalSalida + " "); Int32 EnteroDecimal = (Int32)Math.Round((Double)(Numero - (Int64)Numero) * Math.Pow(10, Decimales), 0); if (ConvertirDecimales) { Boolean esMascaraDecimalDefault = MascaraSalidaDecimal == MascaraSalidaDecimalDefault; Resultado.Append(Convertir((Decimal)EnteroDecimal, 0, null, null, EsMascaraNumerica, false, false, (ApocoparUnoParteDecimal && !EsMascaraNumerica/*&& !esMascaraDecimalDefault*/), false) + " " + (EsMascaraNumerica ? "" : MascaraSalidaDecimal)); } else if (EsMascaraNumerica) Resultado.Append(EnteroDecimal.ToString(MascaraSalidaDecimal)); else Resultado.Append(EnteroDecimal.ToString() + " " + MascaraSalidaDecimal); } //Se pone la primer letra en mayúscula si corresponde y se retorna el resultado if (LetraCapital) return Resultado[1].ToString().ToUpper() + Resultado.ToString(2, Resultado.Length - 2); else return Resultado.ToString().Substring(1); } }
Clase en VB:
Imports System Imports System.Text Imports System.Globalization ''' <summary> ''' Convierte números en su expresión numérica a su numeral cardinal ''' </summary> Public NotInheritable Class Numalet #Region "Miembros estáticos" Private Const UNI As Integer = 0, DIECI As Integer = 1, DECENA As Integer = 2, CENTENA As Integer = 3 Private Shared _matriz As String(,) = New String(CENTENA, 9) { _ {Nothing, " uno", " dos", " tres", " cuatro", " cinco", " seis", " siete", " ocho", " nueve"}, _ {" diez", " once", " doce", " trece", " catorce", " quince", " dieciséis", " diecisiete", " dieciocho", " diecinueve"}, _ {Nothing, Nothing, Nothing, " treinta", " cuarenta", " cincuenta", " sesenta", " setenta", " ochenta", " noventa"}, _ {Nothing, Nothing, Nothing, Nothing, Nothing, " quinientos", Nothing, " setecientos", Nothing, " novecientos"}} Private Const [sub] As Char = CChar(ChrW(26)) 'Cambiar acá si se quiere otro comportamiento en los métodos de clase Public Const SeparadorDecimalSalidaDefault As String = "con" Public Const MascaraSalidaDecimalDefault As String = "00'/100.-'" Public Const DecimalesDefault As Int32 = 2 Public Const LetraCapitalDefault As Boolean = False Public Const ConvertirDecimalesDefault As Boolean = False Public Const ApocoparUnoParteEnteraDefault As Boolean = False Public Const ApocoparUnoParteDecimalDefault As Boolean = False #End Region #Region "Propiedades" Private _decimales As Int32 = DecimalesDefault Private _cultureInfo As CultureInfo = Globalization.CultureInfo.CurrentCulture Private _separadorDecimalSalida As String = SeparadorDecimalSalidaDefault Private _posiciones As Int32 = DecimalesDefault Private _mascaraSalidaDecimal As String, _mascaraSalidaDecimalInterna As String = MascaraSalidaDecimalDefault Private _esMascaraNumerica As Boolean = True Private _letraCapital As Boolean = LetraCapitalDefault Private _convertirDecimales As Boolean = ConvertirDecimalesDefault Private _apocoparUnoParteEntera As Boolean = False Private _apocoparUnoParteDecimal As Boolean ''' <summary> ''' Indica la cantidad de decimales que se pasarán a entero para la conversión ''' </summary> ''' <remarks>Esta propiedad cambia al cambiar MascaraDecimal por un valor que empieze con '0'</remarks> Public Property Decimales() As Int32 Get Return _decimales End Get Set(ByVal value As Int32) If value > 10 Then Throw New ArgumentException(value.ToString() + " excede el número máximo de decimales admitidos, solo se admiten hasta 10.") End If _decimales = value End Set End Property ''' <summary> ''' Objeto CultureInfo utilizado para convertir las cadenas de entrada en números ''' </summary> Public Property CultureInfo() As CultureInfo Get Return _cultureInfo End Get Set(ByVal value As CultureInfo) _cultureInfo = value End Set End Property ''' <summary> ''' Indica la cadena a intercalar entre la parte entera y la decimal del número ''' </summary> Public Property SeparadorDecimalSalida() As String Get Return _separadorDecimalSalida End Get Set(ByVal value As String) _separadorDecimalSalida = value 'Si el separador decimal es compuesto, infiero que estoy cuantificando algo, 'por lo que apocopo el "uno" convirtiéndolo en "un" If value.Trim().IndexOf(" ") > 0 Then _apocoparUnoParteEntera = True Else _apocoparUnoParteEntera = False End If End Set End Property ''' <summary> ''' Indica el formato que se le dara a la parte decimal del número ''' </summary> Public Property MascaraSalidaDecimal() As String Get If Not [String].IsNullOrEmpty(_mascaraSalidaDecimal) Then Return _mascaraSalidaDecimal Else Return "" End If End Get Set(ByVal value As String) 'determino la cantidad de cifras a redondear a partir de la cantidad de '0' o '' 'que haya al principio de la cadena, y también si es una máscara numérica Dim i As Integer = 0 While i < value.Length AndAlso (value(i) = "0"c OrElse value(i) = "#") i += 1 End While _posiciones = i If i > 0 Then _decimales = i _esMascaraNumerica = True Else _esMascaraNumerica = False End If _mascaraSalidaDecimal = value If _esMascaraNumerica Then _mascaraSalidaDecimalInterna = value.Substring(0, _posiciones) + "'" + value.Substring(_posiciones).Replace("''", [sub].ToString()).Replace("'", [String].Empty).Replace([sub].ToString(), "'") + "'" Else _mascaraSalidaDecimalInterna = value.Replace("''", [sub].ToString()).Replace("'", [String].Empty).Replace([sub].ToString(), "'") End If End Set End Property ''' <summary> ''' Indica si la primera letra del resultado debe estár en mayúscula ''' </summary> Public Property LetraCapital() As Boolean Get Return _letraCapital End Get Set(ByVal value As Boolean) _letraCapital = value End Set End Property ''' <summary> ''' Indica si se deben convertir los decimales a su expresión nominal ''' </summary> Public Property ConvertirDecimales() As Boolean Get Return _convertirDecimales End Get Set(ByVal value As Boolean) _convertirDecimales = value _apocoparUnoParteDecimal = value If value Then ' Si la máscara es la default, la borro If _mascaraSalidaDecimal = MascaraSalidaDecimalDefault Then MascaraSalidaDecimal = "" End If ElseIf [String].IsNullOrEmpty(_mascaraSalidaDecimal) Then MascaraSalidaDecimal = MascaraSalidaDecimalDefault 'Si no hay máscara dejo la default End If End Set End Property ''' <summary> ''' Indica si de debe cambiar "uno" por "un" en las unidades. ''' </summary> Public Property ApocoparUnoParteEntera() As Boolean Get Return _apocoparUnoParteEntera End Get Set(ByVal value As Boolean) _apocoparUnoParteEntera = value End Set End Property ''' <summary> ''' Determina si se debe apococopar el "uno" en la parte decimal ''' </summary> ''' <remarks>El valor de esta propiedad cambia al setear ConvertirDecimales</remarks> Public Property ApocoparUnoParteDecimal() As Boolean Get Return _apocoparUnoParteDecimal End Get Set(ByVal value As Boolean) _apocoparUnoParteDecimal = value End Set End Property #End Region #Region "Constructores" Public Sub New() MascaraSalidaDecimal = MascaraSalidaDecimalDefault SeparadorDecimalSalida = SeparadorDecimalSalidaDefault LetraCapital = LetraCapitalDefault ConvertirDecimales = _convertirDecimales End Sub Public Sub New(ByVal ConvertirDecimales As Boolean, ByVal MascaraSalidaDecimal As String, ByVal SeparadorDecimalSalida As String, ByVal LetraCapital As Boolean) If Not [String].IsNullOrEmpty(MascaraSalidaDecimal) Then Me.MascaraSalidaDecimal = MascaraSalidaDecimal End If If Not [String].IsNullOrEmpty(SeparadorDecimalSalida) Then _separadorDecimalSalida = SeparadorDecimalSalida End If _letraCapital = LetraCapital _convertirDecimales = ConvertirDecimales End Sub #End Region #Region "Conversores de instancia" Public Function ToCustomCardinal(ByVal Numero As Double) As String Return Convertir(Convert.ToDecimal(Numero), _decimales, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _ _convertirDecimales, _apocoparUnoParteEntera, _apocoparUnoParteDecimal) End Function Public Function ToCustomCardinal(ByVal Numero As String) As String Dim dNumero As Double If [Double].TryParse(Numero, NumberStyles.Float, _cultureInfo, dNumero) Then Return ToCustomCardinal(dNumero) Else Throw New ArgumentException("'" + Numero + "' no es un número válido.") End If End Function Public Function ToCustomCardinal(ByVal Numero As Decimal) As String Return ToCardinal(Numero) End Function Public Function ToCustomCardinal(ByVal Numero As Int32) As String Return Convertir(Convert.ToDecimal(Numero), 0, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _ _convertirDecimales, _apocoparUnoParteEntera, False) End Function #End Region #Region "Conversores estáticos" Public Shared Function ToCardinal(ByVal Numero As Int32) As String Return Convertir(Convert.ToDecimal(Numero), 0, Nothing, Nothing, True, LetraCapitalDefault, _ ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault) End Function Public Shared Function ToCardinal(ByVal Numero As Double) As String Return Convertir(Convert.ToDecimal(Numero), DecimalesDefault, SeparadorDecimalSalidaDefault, MascaraSalidaDecimalDefault, True, LetraCapitalDefault, _ ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault) End Function Public Shared Function ToCardinal(ByVal Numero As String, ByVal ReferenciaCultural As CultureInfo) As String Dim dNumero As Double If [Double].TryParse(Numero, NumberStyles.Float, ReferenciaCultural, dNumero) Then Return ToCardinal(dNumero) Else Throw New ArgumentException("'" + Numero + "' no es un número válido.") End If End Function Public Shared Function ToCardinal(ByVal Numero As String) As String Return Numalet.ToCardinal(Numero, CultureInfo.CurrentCulture) End Function Public Shared Function ToCardinal(ByVal Numero As Decimal) As String Return ToCardinal(Convert.ToDouble(Numero)) End Function #End Region Private Shared Function Convertir(ByVal Numero As Decimal, ByVal Decimales As Int32, ByVal SeparadorDecimalSalida As String, ByVal MascaraSalidaDecimal As String, ByVal EsMascaraNumerica As Boolean, ByVal LetraCapital As Boolean, _ ByVal ConvertirDecimales As Boolean, ByVal ApocoparUnoParteEntera As Boolean, ByVal ApocoparUnoParteDecimal As Boolean) As String Dim Num As Int64 Dim terna As Int32, centenaTerna As Int32, decenaTerna As Int32, unidadTerna As Int32, iTerna As Int32 Dim cadTerna As String Dim Resultado As New StringBuilder() Num = Math.Floor(Math.Abs(Numero)) If Num >= 1000000000001 OrElse Num < 0 Then Throw New ArgumentException("El número '" + Numero.ToString() + "' excedió los límites del conversor: [0;1.000.000.000.001]") End If If Num = 0 Then Resultado.Append(" cero") Else iTerna = 0 Do Until Num = 0 iTerna += 1 cadTerna = String.Empty terna = Num Mod 1000 centenaTerna = Int(terna / 100) decenaTerna = terna - centenaTerna * 100 'Decena junto con la unidad unidadTerna = (decenaTerna - Math.Floor(decenaTerna / 10) * 10) Select Case decenaTerna Case 1 To 9 cadTerna = _matriz(UNI, unidadTerna) + cadTerna Case 10 To 19 cadTerna = cadTerna + _matriz(DIECI, unidadTerna) Case 20 cadTerna = cadTerna + " veinte" Case 21 To 29 cadTerna = " veinti" + _matriz(UNI, unidadTerna).Substring(1) Case 30 To 99 If unidadTerna <> 0 Then cadTerna = _matriz(DECENA, Int(decenaTerna / 10)) + " y" + _matriz(UNI, unidadTerna) + cadTerna Else cadTerna += _matriz(DECENA, Int(decenaTerna / 10)) End If End Select Select Case centenaTerna Case 1 If decenaTerna > 0 Then cadTerna = " ciento" + cadTerna Else cadTerna = " cien" + cadTerna End If Exit Select Case 5, 7, 9 cadTerna = _matriz(CENTENA, Int(terna / 100)) + cadTerna Exit Select Case Else If Int(terna / 100) > 1 Then cadTerna = _matriz(UNI, Int(terna / 100)) + "cientos" + cadTerna End If Exit Select End Select 'Reemplazo el 'uno' por 'un' si no es en las únidades o si se solicító apocopar If (iTerna > 1 OrElse ApocoparUnoParteEntera) AndAlso decenaTerna = 21 Then cadTerna = cadTerna.Replace("veintiuno", "veintiún") ElseIf (iTerna > 1 OrElse ApocoparUnoParteEntera) AndAlso unidadTerna = 1 AndAlso decenaTerna <> 11 Then cadTerna = cadTerna.Substring(0, cadTerna.Length - 1) 'Acentúo 'veintidós', 'veintitrés' y 'veintiséis' ElseIf decenaTerna = 22 Then cadTerna = cadTerna.Replace("veintidos", "veintidós") ElseIf decenaTerna = 23 Then cadTerna = cadTerna.Replace("veintitres", "veintitrés") ElseIf decenaTerna = 26 Then cadTerna = cadTerna.Replace("veintiseis", "veintiséis") End If 'Completo miles y millones Select Case iTerna Case 3 If Numero < 2000000 Then cadTerna += " millón" Else cadTerna += " millones" End If Case 2, 4 If terna > 0 Then cadTerna += " mil" End Select Resultado.Insert(0, cadTerna) Num = Int(Num / 1000) Loop End If 'Se agregan los decimales si corresponde If Decimales > 0 Then Resultado.Append(" " + SeparadorDecimalSalida + " ") Dim EnteroDecimal As Int32 = Int(Math.Round((Numero - Int(Numero)) * Math.Pow(10, Decimales))) If ConvertirDecimales Then Dim esMascaraDecimalDefault As Boolean = MascaraSalidaDecimal = MascaraSalidaDecimalDefault Resultado.Append(Convertir(Convert.ToDecimal(EnteroDecimal), 0, Nothing, Nothing, EsMascaraNumerica, False, _ False, (ApocoparUnoParteDecimal AndAlso Not EsMascaraNumerica), False) + " " + (IIf(EsMascaraNumerica, "", MascaraSalidaDecimal))) ElseIf EsMascaraNumerica Then Resultado.Append(EnteroDecimal.ToString(MascaraSalidaDecimal)) Else Resultado.Append(EnteroDecimal.ToString() + " " + MascaraSalidaDecimal) End If End If 'Se pone la primer letra en mayúscula si corresponde y se retorna el resultado If LetraCapital Then Return Resultado(1).ToString().ToUpper() + Resultado.ToString(2, Resultado.Length - 2) Else Return Resultado.ToString().Substring(1) End If End Function End Class
Actualización 8/12/2007:
He estado mirando el código (que programé hace ya un par de años, cuando empezaba en .NET) y me he encontrado alguna que otra semi-brutalidad, asique lo toqué un poco. Ahora funciona 30% más rápido.
También cambié el método «ToString» por «ToCardinal» y «ToCustomString» por «ToCustomCardinal», ya que ToString debería transformar en cadena información que ya está en la clase y no un parámetro que recibe.
Esto no afecta el resultado del algoritmo que sigue siendo exactamente el mismo.
Para terminar agregué un par de ejemplos.
Saludos.
Muchas Gracias, el trabajo es completo sin fallas, eso si el cambio que tu comentas aun esta en el Zip, que es disponible para descargar, si lo actualizas seria bueno, gracias de nuevo me sacastes de un problema jaja.
Hola amigo buenas noches mi pregunta como lo implemento en un reportview para poner el nuemro en letras gracias pro tu respuesta
Hohoho…. muchas gracias compadre, esto me ha salvado el dia, ando a las carreras haciendo un pequeño módulo de facturación, necesitaba ésto y funciona perfecto!.
Gracias por compartir!
saludos, no puedo bajar la aplicacion «NumaletNet.rar «, por favor alquien podria enviarlo a mi correo..?
JOSETITOROJASPANIAGUA@HOTMAIL.COM
gracias de antemano
Tito, acabo de probar el link que está al principio del artículo y funciona ok.
Gracias mrrookie.
Saber que alguien aprovecha lo que publico es gratificante.
¡Saludos!
Te pasaste !!
Que buena libreria aun que la he modificado un poco pero funciona de mil maravillas gracias por tu colaboracion me ahorras mucho tiempo de programacion
¡Gracias Richard!
Me alegro que te haya sido util. Para vos y los que modifiquen el código en el futuro, si los cambios no son específicos de su aplicación, les agradecería que me los comenten para ver si se puede corregir o ampliar el código.
Saludos: Alejandro
Hola
Una preguntita: ¿El codigo que publicas es una clase en visual basic ?
Y me podrias decir como la uso dentro de un formulario, es decir, como la instancio o como la mando llamar para que me realice las acciones en el formulario:
el codigo que uso es:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TB_letra.Text = «»
If IsNumeric(TB_numero.Text) Then
TB_letra.Text = Convertir(TB_numero.Text)
Else
MessageBox.Show(«Ingrese por favor números», «Aviso», MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
TB_numero.Focus()
TB_numero.SelectionStart = 0
TB_numero.SelectionLength = TB_numero.ToString.Length
End Sub
pero me dice que no he declarado la variable convertir y se supone que ese es el nombre de la función.
De antemano, gracias por tu ayuda
Silvia:
Si reemplazaras:
TB_letra.Text = Convertir(TB_numero.Text)
por:
TB_letra.Text = Numalet.ToCardinal(TB_numero.Text)
Debería funcionar.
Si mirás el código, ‘Convertir’ es un método privado, o sea que es para uso interno de la clase. El método público es ‘ToCardinal’, que es el que tenés que usar.
Aparte, el hecho de que el método sea estático (Shared) implica que lo podés llamar sin instanciar la clase, pero tenés que nombrarla antes, si no el compilador no sabe de que clase tiene que sacar el método.
Te comento por las dudas que la palabra ‘método’ en OOP = ‘Function’ o ‘Sub’ en VB.
Saludos y espero que con esto lo puedas hacer funcionar.
Hey solo para agradecer tu excelente Clase.
Lo único que necesite cambiar son los textos en mayúsculas y algunas nimiendades.
Gracias Al!, estoy corto de tiempo y por eso no estoy publicando, pero cualquier cambio u observación te agradecería que me lo envíes así lo corrijo en cuanto tenga tiempo.
Saludos
Muy bueno.! 🙂
Son las 4:30 de la madrugada.. pensaba aventarme el algoritmo, pero quise darle un ojo antes a google para ver si ya existía algo.. y definitivamente tuve bastante suerte.. encontré tú código y despues de probarlo no me queda más que agradecer 🙂
Gracias y saludos 🙂
Muchas gracias por compartir, esto ahorra mucho tiempo, los hombres grandes se hacen más grandes al compartir el conocimiento.
Leonardo, Phylevn y todos los lectores de VarioNet: Me alegro si la información que aquí encuentran les es util, esa es la idea. Como pueden ver el blog está desactualizado debido a trabajo + vacaciones + la muerte de mi motherboard + etc. Pero prometo publicar relativamente pronto algunos artículos que ya estoy preparando. Saludos!
yo quiero una fincion que me convierta una cantidad numerica a letras asi como ese ejemplo pero para php
María: Lamentablemente no domino php, pero mirando el código tanto en vb como en c# creo que no te será muy dificil convertir la función ya que el algoritmo no es muy complicado. ¡Suerte!
Hey man no entendi, lo necesito hacer para c# te puedes comunicar conmigo, espero que no sea molestia, gracias
Hola Dk. Te agradecería que pongas tus dudas como comentario de la entrada para que queden como referencia para los demás.
Gracias y saludos
Para que visual studio es este codigo? 2005?
Dk:
Funciona en 2005, 2008 y no lo probé, pero debería funcionar en 2003.
Saludos
tengo visual 2003 pero e probado otros codigos y no jala…
Dk, Lamentablemente no tengo VS2003 y no veo en el código que puede ser lo que no funcione.
Con una descripción de por que no compila tal vez pueda ayudarte.
Saludos
Hombre!! Aplausos!!! Tienes razón, hay muchísimos algoritmos por ahi por Google… pero hasta ahorita ninguno como el tuyo, felicidades.. sobre todo por hacerlo tan orientado a objetos…. hay unos jeroglificos por ahi.. que para que te cuento.. Mil gracias!
Bueno la verdad logre que me funcionara otro codigo, este es muy complicado, pero de todas formas muchas gracias.
using System;
using System.Collections.Generic;
using System.Text;
namespace Conversiones
{
class Conv
{
public string enletras(string num)
{
string res, dec = «»;
Int64 entero;
int decimales;
double nro;
try
{
nro = Convert.ToDouble(num);
}
catch
{
return «»;
}
entero = Convert.ToInt64(Math.Truncate(nro));
decimales = Convert.ToInt32(Math.Round((nro – entero) * 100, 2));
if (decimales > 0)
{
dec = » CON » + decimales.ToString() + «/100»;
}
res = toText(Convert.ToDouble(entero)) + dec;
return res;
}
private string toText(double value)
{
string Num2Text = «»;
value = Math.Truncate(value);
if (value == 0) Num2Text = «CERO»;
else if (value == 1) Num2Text = «UNO»;
else if (value == 2) Num2Text = «DOS»;
else if (value == 3) Num2Text = «TRES»;
else if (value == 4) Num2Text = «CUATRO»;
else if (value == 5) Num2Text = «CINCO»;
else if (value == 6) Num2Text = «SEIS»;
else if (value == 7) Num2Text = «SIETE»;
else if (value == 8) Num2Text = «OCHO»;
else if (value == 9) Num2Text = «NUEVE»;
else if (value == 10) Num2Text = «DIEZ»;
else if (value == 11) Num2Text = «ONCE»;
else if (value == 12) Num2Text = «DOCE»;
else if (value == 13) Num2Text = «TRECE»;
else if (value == 14) Num2Text = «CATORCE»;
else if (value == 15) Num2Text = «QUINCE»;
else if (value < 20) Num2Text = «DIECI» + toText(value – 10);
else if (value == 20) Num2Text = «VEINTE»;
else if (value < 30) Num2Text = «VEINTI» + toText(value – 20);
else if (value == 30) Num2Text = «TREINTA»;
else if (value == 40) Num2Text = «CUARENTA»;
else if (value == 50) Num2Text = «CINCUENTA»;
else if (value == 60) Num2Text = «SESENTA»;
else if (value == 70) Num2Text = «SETENTA»;
else if (value == 80) Num2Text = «OCHENTA»;
else if (value == 90) Num2Text = «NOVENTA»;
else if (value < 100) Num2Text = toText(Math.Truncate(value / 10) * 10) + » Y » + toText(value % 10);
else if (value == 100) Num2Text = «CIEN»;
else if (value < 200) Num2Text = «CIENTO » + toText(value – 100);
else if ((value == 200) || (value == 300) || (value == 400) || (value == 600) || (value == 800)) Num2Text = toText(Math.Truncate(value / 100)) + «CIENTOS»;
else if (value == 500) Num2Text = «QUINIENTOS»;
else if (value == 700) Num2Text = «SETECIENTOS»;
else if (value == 900) Num2Text = «NOVECIENTOS»;
else if (value < 1000) Num2Text = toText(Math.Truncate(value / 100) * 100) + » » + toText(value % 100);
else if (value == 1000) Num2Text = «MIL»;
else if (value < 2000) Num2Text = «MIL » + toText(value % 1000);
else if (value 0) Num2Text = Num2Text + » » + toText(value % 1000);
}
else if (value == 1000000) Num2Text = «UN MILLON»;
else if (value < 2000000) Num2Text = «UN MILLON » + toText(value % 1000000);
else if (value 0) Num2Text = Num2Text + » » + toText(value – Math.Truncate(value / 1000000) * 1000000);
}
else if (value == 1000000000000) Num2Text = «UN BILLON»;
else if (value 0) Num2Text = Num2Text + » » + toText(value – Math.Truncate(value / 1000000000000) * 1000000000000);
}
return Num2Text;
}
}
}
38750000
DK: Si te sirvió…¡Problema solucionado!
Gracias por incluir como resolviste el problema.
Saludos
Gracias mi hermano, la verad eres un programador con mucha experiencia
HOLA A TODOS ESTA BIEN EL CODIGO, PERO ALGUIEN SABE COMO HACERLO EN MODO CONSOLE EN WINDOWS, USO VISUALSTUDIO 2005, SE LOS AGRADECERIA MUCHO
Fulanito: No hay diferencia entre los tipos de proyecto, consola, web, winform, lo que quieras. Incluí la clase y las llamadas te retornan un string:
Console.Write(Numalet.ToCardinal(«155.38», new CultureInfo(«en-US»)));
Saludos: Alejandro
Muy buen algoritmo, muchas gracias por ser tan claro al expresar el codigo
Saludos
Gustavo
Excelente algoritmo, muy completo y muy util, me salvaste muchas horas de programación.
En general el blog esta muy bueno, hay mucha información que es practicamente imposible encontrar en libros o en la ayuda del visual studio.
Gracias.
Hola, Gracias por tu trabajo.
Tengo un problema. Yo estoy trabajando con visual studio 2003 y solo hay una parte del código que no funciona.
Dentro de la propiedad «Public Property MascaraSalidaDecimal() As String», donde se establece «Set(ByVal value As String)» al compilar da un error,en la linea While i < value.Length AndAlso (value(i) = «0»c) Or value(i) = «»
donde dice que «value» no es una matriz o un método y no puede tener tener una lista de argumentos. He intantado sustituir la línea por:
While i < value.Length AndAlso ((Mid(value, i, 1)) = «0»c) Or Mid(value, i, 1) = «»
pero claro i debe ser mayor que cero para poder evaluar la expresion y el resto del código funciona si i=0 o mayor.
Alguna idea para solucionarlo??
Gracias.
Jose
La Coruña
España.
josé: No tengo un VS 2003 para probar, se me ocurren dos cosas:
1)Probaría: While i < value.Length AndAlso (value.ToCharArray()(i) = «0»c) OrElse value.ToCharArray()(i) = «»
Esto es nada más otra forma de escribir lo mismo, y no se si te ayudará, a falta de compilador, espero tu comentario.
2) En .net 2.0, And y Or deberían ser reemplazados por AndAlso y OrElse (je, Al ‘Or’ de la sentencia debería agregársele el ‘Else’ que me comí y que todavía estoy digiriendo), ya que de esa manera no se evalúa el resto de la sentencia si alguno de los términos anteriores determina el valor de verdad de la misma.
Me explico: si en la evaluación de «a AndAlso b», a da como resultado false, NO se evalúa b, utilizando And se evalúa igualmente b. Este es el comportamiento por defecto en C# y el recomendado en VB, así sentencias como:
If (miDataSet.Tables.Count > 0 AndAlso miDataSet.Tables[0].Rows.Count > 0)…
no fallarán aunque no haya DataTables, lo que si harían utilizando «And» (al evaluar la colección de rows de un DataTable que no existe).
No recuerdo que estos operadores estén disponibles en el fw1.1 y en la msdn indica a partir del 2.0, entiendo que no debería compilar la sentencia que pusiste con AndAlso en fw 1.1, pero vos me dirás si es así.
Saludos y suerte, si te funciona avisame así lo dejo publicado para todas las versiones del fw.
Alejandro
Hola Alejandro, buenísima esta librería. Gracias por todo lo que me has ahorrado.
Tengo una duda, referente a un error que me marca: estoy utilizando VB 9 y en
let = Nothing
let = new Numalet();
me marca que Let ya no es compatible con esta versión.
Me pudieras decir qué es lo que puedo utilizar para solucionar este problemilla. De antemano, muchas gracias.
Estoy utilizando 2012 y me corrió a la perfección la clase
Gracias Alejandro por tu respuesta.
Yo he probado con la línea de abajo, pero siempre obtengo un error ya que al final del bucle, i siempre es mayor que la propiedad lengh del string value.
While i < value.Length AndAlso (value.Substring(i) = «0»c) Or value.Substring(i) = «»
Con la línea que sugieres, tambien obtengo un error de compilacion, don de dice que no se pueden guardar los datos «UNICODE».
Otra duda, pero ya del propio funcionamiento.
Aunque ya hace muchos años que mis clases de Castellano se me han olvidado, la clase cuando traduce, por ejemplo 1798,12 da como salida
UN MIL SETECIENTOS NOVENTA Y OCHO CON DOCE. En mi humilde opinion creo que el «UN» sobraría.
José:
Respecto del error, necesitaríamos alguien con VS 2003 que nos de una mano. En cuanto al bucle, si la condición es While i > value.length, nunca podría ser i igual o mayor que value.length!
En cuanto a la manera de escribir el número, la manera correcta es incluyendo «un», siempre según la Real Academia (el link está en el artículo), y si bien es normal no incluirlo en el uso diarío, si cuando se escriben importes, ¡no sería la primera vez que alguien agregue un «siete» delante del mil y un 1 se convierta en 7! 🙂
Saludos y si veo la manera de arreglarlo, lo posteo, te pido lo mismo para dejar el codigo funcional en todos los fw.
Alejandro
No se que veas primero si mis coments o mi mensaje… pero ya solucione esta parte..
switch (iTerna)
{
case 3:
if (Num 0)
{
if (cadTerna == » un»)
{
cadTerna = » mil»;
}
else
{
cadTerna += » mil»;
}
}
break;
}
hola tengo una pregunta, cuando en un for (VB) pregunta la clave y despues de 3 intentos manda un msgbox donde pone el resultado de intentos en forma de numero (numerointentos – veces) = 2 o 3, como hago para que esto se vea en letras
MessageBox.Show(«Incorrect User Name and Password attempts remaining » & (NumeroIntentos – veces) & » more»)
ahora
incorrect User Name amd Password attempts remaining 2 more
lo que quiero se ve asi
incorrect User Name amd Password attempts remaining two more
de ante mano gracias por la atencion
Hola solo quisiera preguntar como puedo hacer el metodo main pra q me funcione, no se mucho de programacion y me han dejado la tarea de convertir numeros la letras y probe el programa pero no me corre porque mefalta el metodo main, pense en algo como esto epro no me funciona:
public static void Main(string[] args)
{
Console.WriteLine(«Ingrese un Numero Entero»);
int Numero = 0;
string resultado =»»;
Numero=Convert.ToInt32(Console.ReadLine());
resultado=Numalet(Numero);
Console.WriteLine(resultado);
Console.ReadLine();
Gabriela, la sentencia debería ser:
Numalet.ToCardinal(Numero);
¡Saludos!
Para los que no saben en c# como agregar la clase de arriba (no menos preciando a nadie 🙂 ) es simple solo deben crear su formulario si quieren un 2 textbox y ya . luego ponen agregar elemetno nuevo y eligen clase. pegan el codigo de arriba en la nueva clase borrando todo lo que habia ahi escrito.
luego van al form y le dan doble click al textbox1 y en el codigo ponen esto:
private void textBox1_TextChanged(object sender, EventArgs e)
{
try
{
decimal numero = Decimal.Parse(textBox1.Text);
// MessageBox.Show(Numalet.ToCardinal(temp));
textBox2.Text = Numalet.ToCardinal(numero);
}
catch
{
textBox2.Clear();
}
}
y listo con eso les va a funcionar es facil. PEro recomiendo leer y analizar el codigo de Alejandro para asi entender mejor y adaptarlo a sus necesidades.
Gracias Alejandro
Gracias El anónimo X) por el ejemplo!
Excelente Ejemplo Brother
Muchas felicidades por esta ayuda !!!
ta bien!! lo hize de examen sake 100 😀
al raton!!
Hola, impresionante tu codigo!… muy bueno la verdad, muchas gracias por publicarlo!…
Gracias Dig! me alegro de que te sea util.
¡Saludos!
hola alejandro que tal, yo solo necesito hacer que funcione en modo consola, poner un numero y que me despliegue en letras el numero que es….. copio y pego tu codigo y me sale en el debugueador. Program ‘C:Documents and setting/RMEDINA/Mis doctos…………ConsoleApplication31.exe’ does not contain a static ‘Main’ method suitable for an enttry point …… la verdad yo soy muy novato en esto de la programacion y por cuestion laborales estoy aprendiendo…. me podrias ayudar? ya sea agregandome al msn o no se.. te lo agradeceria DEMASIADO
Russell:
Entiendo que al copiar mi clase lo estás haciendo sobre la clase ‘Program’ del archivo Program.cs de tu proyecto.
Esta clase contiene el método ‘Main’ que te pide el compilador y que es el punto de inicio del programa, por lo que no hay que tocarlo.
El procedimiento correcto más sencillo sería agregar una nueva clase ‘Numalet’ (botón derecho sobre el proyecto en Visual Studio), ahí si reemplazar el código por el de la clase Numalet que te bajas del blog y, en el interior del método ‘Main’ de la clase ‘Program’, escribir algo así como:
String EntradaUsuario = Console.ReadLine();
Console.WriteLine(Numalet.ToCardinal(EntradaUsuario));
Console.Read();
En C#, para este ejemplo, si trabajas en visual basic, sería:
Dim EntradaUsuario As String = Console.ReadLine()
Console.WriteLine(Numalet.ToCardinal(EntradaUsuario))
Console.Read()
pero en este caso el método Main se encuentra en el archivo Module1.vb.
Respecto a aprender programación, te recomiendo el sitio de la msdn: (http://msdn.microsoft.com/es-es/default.aspx) y
el de El Guille (http://www.elguille.info)
¡Suerte!
Saludos, sabes es impresionante la limpieza con la que trabajas.. Me gusto mucho tu codigo.. solo hay un detalle, bueno.. yo soy de mexico… y aca nadie dice UN mil solo dices mil…
creo que es lo unico que le note mal..
en fin que estes muy bien.. cuidate
PARA LOS QUE VIVAN CON EL MISMO PROBLEMA DEL «UN MIL» CON ESTO SE SOLUCIONA, ES LA LINEA 331 DE LA CLASE, SOLO ESA LINEA.. TODO LO DEMÀS ESTA MAS QUE MAGNIFICO…
switch (iTerna)
{
case 3:
if (Num 0)
{
if (cadTerna == » un»)
{
cadTerna = » mil»;
}
else
{
cadTerna += » mil»;
}
}
break;
}
Edgar, gracias por el comentario.
«Un mil» es la manera indicada por la Real Academia, pero en la mayoría de los países de habla hispana (si no en todos) tampoco se pronuncia en el habla corriente «Un mil», sin embargo es una cuestión de uso y costumbre al escribir importes en documentos con caracter legal:
Pensemos que es facil agregar un «nueve» delante de la palabra «mil» en un cheque, y también hacer un «pancita» en un 1, convirtiendo 1.234,00 en 9.234,00.
Claro que esto es más importante cuando se escribe a mano, pero como dice por ahí: «Camarón que se duerme, se lo lleva la corriente».
Saludos!
Tienes razón en eso, sin embargo el cliente manda. Y si al cliente no le gusta que diga un mil pues no hay mucho que hacer.. jejejeje
Y pues la soluciòn es solo para aquellos que asi lo rquieran y que no puedan hacer entrar en razon a sus clients..
Aunque tambièn es cierto que la real academia de la lengua no ha evolucionado mucho desde hace muchisimos años, ya que hay muchisimas palabras en desuso en méxico..
adivina que.. encontre un algo que no estoy muy seguro si es error
cantidad
1001000
un millón un mil con 00/100.-
amm tambien encontre otra cosa.. cuando escribe el numero veintiuno a veces lo acentua a veces no…
Edgar, de nuevo gracias por los comentarios.
Veamos, en el caso de 1.001.000, creo que el resultado es correcto en función del tema que ya hablamos sobre el «un mil». Para tener otro comportamiento hay que tocar el código.
Respecto al 21 te agradecería algún ejemplo que de acentuado y otro que no.
Por las dudas que sea eso te comento que hay un tema en castellano y es que los números se escriben distinto en función del contexto en que se usan.
Como dice el artículo: «en vez de poner “treinta y uno elefantes” ponemos “treinta y un elefantes”». Para trabajar con esto se ofrecen las propiedades ApocoparUnoParteEntera y ApocoparUnoParteDecimal, explicadas en el texto.
También siguiendo la misma linéa ocurre que, cito el artículo:
«El código tiene en cuenta que: […] Cuando cuantificamos miles, cientos de miles o millones, hay que apocopar la palabra o terminación “uno”, por ejemplo 21 es “veintiuno”, pero 21.000 es “veintiún mil”.»
Como puedes ver «veintiuno» no lleva acento pero «veintiún» si lo hace.
Ya se que la API de la clase no es muy linda, pero quise ofrecer la mayor cantidad de posibilidades pensando en los diferentes usos y costumbres de los países y en usos para cantidades no solo de dinero sino de elementos tangibles (aunque no creo que nadie lo haya usado para elefantes 🙂 ).
Note: Para hacer uso de este tipo de ajustes, hay que instanciar la clase y setear las propiedades, o bien cambiar los valores por defecto en el código.
Edgar, nuevamente gracias por las pruebas y por reportar lo que encuentras, si no es el caso que te comento aquí lo que detectaste, por favor no dejes de informarme. Un saludo: Alejandro
Excelente el Código, Funcionó a la perfección.
¡Gracias victor! me alegra que te sea util.
Hola Alejandro,
Muchas gracias por compartir tu código. Lo acabo de implementar con las modificaciones necesarias para que el texto sea en catalán y funciona perfecto. Si por un casual lo necesitas te paso la clase modificada cuando quieras.
Para lo del ‘un mil’ (que en catalán siempre es mil sin el ‘un’ yo hice una pequeña chapuza. Justo antes del condicional para capitalizar el Resultado he metido la siguiente línea:
Resultado.Replace(«un mil», «mil»);
Grácias de nuevo y un saludo,
slimer
slimer:
Bienvenido tu código. Si no te molesta enviármelo, quisiera incluirlo en el post, seguramente le será útil a muchos desarrolladores.
Saludos: Alejandro
Ok. Te lo C&P aquí. La verdad es que no varía mucho:
using System;
using System.Text;
using System.Globalization;
///
/// Convierte números en su expresión numérica a su numeral cardinal
///
public sealed class Numalet
{
#region Miembros estáticos
private const int UNI = 0, DIECI = 1, DECENA = 2, CENTENA = 3;
private static string[,] _matriz = new string[CENTENA + 1, 10]
{
{null,» un», » dos», » tres», » quatre», » cinc», » sis», » set», » vuit», » nou»},
{» deu»,» onze»,» dotze»,» tretze»,» catorze»,» quinze»,» setze»,» disset»,» divuit»,» dinou»},
{null,null,null,» trenta»,» quaranta»,» cinquanta»,» seixanta»,» setanta»,» viutanta»,» noranta»},
{null,null,null,null,null,» cinc-cents»,null,» set-cents»,null,» nou-cents»}
};
private const Char sub = (Char)26;
//Cambiar acá si se quiere otro comportamiento en los métodos de clase
public const String SeparadorDecimalSalidaDefault = «amb»;
public const String MascaraSalidaDecimalDefault = «00’/100.-‘»;
public const Int32 DecimalesDefault = 2;
public const Boolean LetraCapitalDefault = false;
public const Boolean ConvertirDecimalesDefault = false;
public const Boolean ApocoparUnoParteEnteraDefault = false;
public const Boolean ApocoparUnoParteDecimalDefault = false;
#endregion
#region Propiedades
private Int32 _decimales = DecimalesDefault;
private CultureInfo _cultureInfo = CultureInfo.CurrentCulture;
private String _separadorDecimalSalida = SeparadorDecimalSalidaDefault;
private Int32 _posiciones = DecimalesDefault;
private String _mascaraSalidaDecimal, _mascaraSalidaDecimalInterna = MascaraSalidaDecimalDefault;
private Boolean _esMascaraNumerica = true;
private Boolean _letraCapital = LetraCapitalDefault;
private Boolean _convertirDecimales = ConvertirDecimalesDefault;
private Boolean _apocoparUnoParteEntera = false;
private Boolean _apocoparUnoParteDecimal;
///
/// Indica la cantidad de decimales que se pasarán a entero para la conversión
///
/// Esta propiedad cambia al cambiar MascaraDecimal por un valor que empieze con ‘0’
public Int32 Decimales
{
get { return _decimales; }
set
{
if (value > 10) throw new ArgumentException(value.ToString() + » excede el número máximo de decimales admitidos, solo se admiten hasta 10.»);
_decimales = value;
}
}
///
/// Objeto CultureInfo utilizado para convertir las cadenas de entrada en números
///
public CultureInfo CultureInfo
{
get { return _cultureInfo; }
set { _cultureInfo = value; }
}
///
/// Indica la cadena a intercalar entre la parte entera y la decimal del número
///
public String SeparadorDecimalSalida
{
get { return _separadorDecimalSalida; }
set
{
_separadorDecimalSalida = value;
//Si el separador decimal es compuesto, infiero que estoy cuantificando algo,
//por lo que apocopo el «uno» convirtiéndolo en «un»
if (value.Trim().IndexOf(» «) > 0)
_apocoparUnoParteEntera = true;
else _apocoparUnoParteEntera = false;
}
}
///
/// Indica el formato que se le dara a la parte decimal del número
///
public String MascaraSalidaDecimal
{
get
{
if (!String.IsNullOrEmpty(_mascaraSalidaDecimal))
return _mascaraSalidaDecimal;
else return «»;
}
set
{
//determino la cantidad de cifras a redondear a partir de la cantidad de ‘0’ o ‘#’
//que haya al principio de la cadena, y también si es una máscara numérica
int i = 0;
while (i 0)
{
_decimales = i;
_esMascaraNumerica = true;
}
else _esMascaraNumerica = false;
_mascaraSalidaDecimal = value;
if (_esMascaraNumerica)
_mascaraSalidaDecimalInterna = value.Substring(0, _posiciones) + «‘»
+ value.Substring(_posiciones)
.Replace(«»», sub.ToString())
.Replace(«‘», String.Empty)
.Replace(sub.ToString(), «‘») + «‘»;
else
_mascaraSalidaDecimalInterna = value
.Replace(«»», sub.ToString())
.Replace(«‘», String.Empty)
.Replace(sub.ToString(), «‘»);
}
}
///
/// Indica si la primera letra del resultado debe estár en mayúscula
///
public Boolean LetraCapital
{
get { return _letraCapital; }
set { _letraCapital = value; }
}
///
/// Indica si se deben convertir los decimales a su expresión nominal
///
public Boolean ConvertirDecimales
{
get { return _convertirDecimales; }
set
{
_convertirDecimales = value;
_apocoparUnoParteDecimal = value;
if (value)
{// Si la máscara es la default, la borro
if (_mascaraSalidaDecimal == MascaraSalidaDecimalDefault)
MascaraSalidaDecimal = «»;
}
else if (String.IsNullOrEmpty(_mascaraSalidaDecimal))
//Si no hay máscara dejo la default
MascaraSalidaDecimal = MascaraSalidaDecimalDefault;
}
}
///
/// Indica si de debe cambiar «uno» por «un» en las unidades.
///
public Boolean ApocoparUnoParteEntera
{
get { return _apocoparUnoParteEntera; }
set { _apocoparUnoParteEntera = value; }
}
///
/// Determina si se debe apococopar el «uno» en la parte decimal
///
/// El valor de esta propiedad cambia al setear ConvertirDecimales
public Boolean ApocoparUnoParteDecimal
{
get { return _apocoparUnoParteDecimal; }
set { _apocoparUnoParteDecimal = value; }
}
#endregion
#region Constructores
public Numalet()
{
MascaraSalidaDecimal = MascaraSalidaDecimalDefault;
SeparadorDecimalSalida = SeparadorDecimalSalidaDefault;
LetraCapital = LetraCapitalDefault;
ConvertirDecimales = _convertirDecimales;
}
public Numalet(Boolean ConvertirDecimales, String MascaraSalidaDecimal, String SeparadorDecimalSalida, Boolean LetraCapital)
{
if (!String.IsNullOrEmpty(MascaraSalidaDecimal))
this.MascaraSalidaDecimal = MascaraSalidaDecimal;
if (!String.IsNullOrEmpty(SeparadorDecimalSalida))
_separadorDecimalSalida = SeparadorDecimalSalida;
_letraCapital = LetraCapital;
_convertirDecimales = ConvertirDecimales;
}
#endregion
#region Conversores de instancia
public String ToCustomCardinal(Double Numero)
{ return Convertir((Decimal)Numero, _decimales, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _convertirDecimales, _apocoparUnoParteEntera, _apocoparUnoParteDecimal); }
public String ToCustomCardinal(String Numero)
{
Double dNumero;
if (Double.TryParse(Numero, NumberStyles.Float, _cultureInfo, out dNumero))
return ToCustomCardinal(dNumero);
else throw new ArgumentException(«‘» + Numero + «‘ no es un número válido.»);
}
public String ToCustomCardinal(Decimal Numero)
{ return ToCardinal((Numero)); }
public String ToCustomCardinal(Int32 Numero)
{ return Convertir((Decimal)Numero, 0, _separadorDecimalSalida, _mascaraSalidaDecimalInterna, _esMascaraNumerica, _letraCapital, _convertirDecimales, _apocoparUnoParteEntera, false); }
#endregion
#region Conversores estáticos
public static String ToCardinal(Int32 Numero)
{
return Convertir((Decimal)Numero, 0, null, null, true, LetraCapitalDefault, ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault);
}
public static String ToCardinal(Double Numero)
{
return ToCardinal((Decimal)Numero);
}
public static String ToCardinal(String Numero, CultureInfo ReferenciaCultural)
{
Double dNumero;
if (Double.TryParse(Numero, NumberStyles.Float, ReferenciaCultural, out dNumero))
return ToCardinal(dNumero);
else throw new ArgumentException(«‘» + Numero + «‘ no es un número válido.»);
}
public static String ToCardinal(String Numero)
{
return Numalet.ToCardinal(Numero, CultureInfo.CurrentCulture);
}
public static String ToCardinal(Decimal Numero)
{
return Convertir(Numero, DecimalesDefault, SeparadorDecimalSalidaDefault, MascaraSalidaDecimalDefault, true, LetraCapitalDefault, ConvertirDecimalesDefault, ApocoparUnoParteEnteraDefault, ApocoparUnoParteDecimalDefault);
}
#endregion
private static String Convertir(Decimal Numero, Int32 Decimales, String SeparadorDecimalSalida, String MascaraSalidaDecimal, Boolean EsMascaraNumerica, Boolean LetraCapital, Boolean ConvertirDecimales, Boolean ApocoparUnoParteEntera, Boolean ApocoparUnoParteDecimal)
{
Int64 Num;
Int32 terna, centenaTerna, decenaTerna, unidadTerna, iTerna;
String cadTerna;
StringBuilder Resultado = new StringBuilder();
Num = (Int64)Math.Abs(Numero);
if (Num >= 1000000000000 || Num 0)
{
iTerna++;
cadTerna = String.Empty;
terna = (Int32)(Num % 1000);
centenaTerna = (Int32)(terna / 100);
decenaTerna = terna % 100;
unidadTerna = terna % 10;
if ((decenaTerna > 0) && (decenaTerna = 10) && (decenaTerna 20) && (decenaTerna = 30) && (decenaTerna 0) cadTerna = » cent» + cadTerna;
else cadTerna = » cent» + cadTerna;
break;
case 5:
case 7:
case 9:
cadTerna = _matriz[CENTENA, (Int32)(terna / 100)] + cadTerna;
break;
default:
if ((Int32)(terna / 100) > 1) cadTerna = _matriz[UNI, (Int32)(terna / 100)] + «-cents» + cadTerna;
break;
}
// Zona comentada per que en català no cal.
//Reemplazo el ‘uno’ por ‘un’ si no es en las únidades o si se solicító apocopar
//if ((iTerna > 1 | ApocoparUnoParteEntera) && decenaTerna == 21)
cadTerna = cadTerna.Replace(«vint-i-un», «vint-i-un»);
//else if ((iTerna > 1 | ApocoparUnoParteEntera) && unidadTerna == 1 && decenaTerna != 11)
// cadTerna = cadTerna.Substring(0, cadTerna.Length – 1);
//Acentúo ‘veintidós’, ‘veintitrés’ y ‘veintiséis’
//else if (decenaTerna == 22) cadTerna = cadTerna.Replace(«vint-i-dos», «vint-i-dos»);
//else if (decenaTerna == 23) cadTerna = cadTerna.Replace(«vint-i-tres», «vint-i-tres»);
//else if (decenaTerna == 26) cadTerna = cadTerna.Replace(«vint-i-sis», «vint-i-sis»);
//Completo miles y millones
switch (iTerna)
{
case 3:
if (Numero 0) cadTerna += » mil»;
break;
}
Resultado.Insert(0, cadTerna);
Num = (Int32)(Num / 1000);
} //while
}
//Se agregan los decimales si corresponde
if (Decimales > 0)
{
Resultado.Append(» » + SeparadorDecimalSalida + » «);
Int32 EnteroDecimal = (Int32)Math.Round((Double)(Numero – (Int64)Numero) * Math.Pow(10, Decimales), 0);
if (ConvertirDecimales)
{
Boolean esMascaraDecimalDefault = MascaraSalidaDecimal == MascaraSalidaDecimalDefault;
Resultado.Append(Convertir((Decimal)EnteroDecimal, 0, null, null, EsMascaraNumerica, false, false, (ApocoparUnoParteDecimal && !EsMascaraNumerica/*&& !esMascaraDecimalDefault*/), false) + » »
+ (EsMascaraNumerica ? «» : MascaraSalidaDecimal));
}
else
if (EsMascaraNumerica) Resultado.Append(EnteroDecimal.ToString(MascaraSalidaDecimal));
else Resultado.Append(EnteroDecimal.ToString() + » » + MascaraSalidaDecimal);
}
//En català no es fa servir un mil si no només mil
Resultado.Replace(«un mil», «mil»);
//Se pone la primer letra en mayúscula si corresponde y se retorna el resultado
if (LetraCapital)
return Resultado[1].ToString().ToUpper() + Resultado.ToString(2, Resultado.Length – 2);
else
return Resultado.ToString().Substring(1);
}
}
Realmente weno el Post era lo que estaba buscando.Te ha quedado muy buena la clase.
Gracias
Alejandro, quiero felicitarte por tu codigo, no solamente porque es una de las mejores clases que he visto (facil de utilizar, flexible, comprensible y eficiente… realmente excelente) sino tambien por publicarla y compartirla con el mundo. Estoy seguro que, al igual que a mi, ahorraste mucho tiempo de programacion a mucha gente. Muchisimas gracias por ayudarnos a todos con tu experiencia y felicidades nuevamente.
Marco, Maykel: Me alegro de que les haya servido el código. La satisfacción de que lo que hice sea útil es la compensación por el trabajo que pudo haber llevado.
Saludos!
Excelente, me has ahorrado como 2 hrs de programación y tengo poco tiempo disponible.
Muy agradecido. Si te gusta la música pásate por mi blog, seguro encuentras algo que te gusta.
Saludos!
Amigo, te felicito por el codigo, me funciona a la perfeccion. Muchas gracias por tu aporte!!!
Gracias Ignacio, me alegro que te sea util!
Muy buen aporte, Gracias… me fue de mucha ayuda!! xD
Hola, te agradezco realmente tu aporte ya que es justo lo que necesito para realizar una tarea en mi trabajo. Mi única duda es si existe alguna manera de que cuando convierto un número entero (sin decimales) y tengo seteada la propiedad ConvertirDecimales en true, no me escriba al final la frase «con cero». Por ejemplo:
Numalet let = new Numalet();
let.ConvertirDecimales = true;
MessageBox.Show(let.ToCustomCardinal(21));
//veintiuno con cero
Existe la forma de que solo retorne «veintiuno» ??
Martín, la propiedad «ConvertirDecimales» indica si los decimales deben aparecer como cardinal, por ejemplo 10,15 como «diez con quince» o como «diez con 15».
A priori hay una sobrecarga estática de ToCardinal que recibe un entero y da el resultado que creo que te sirve:
Numalet.ToCardinal(21)
te retornará «veintiuno».
¡Saludos y gracias!
hola alejandro estoy tratando de hacer funcionar pero me sale el siguiente error:
Falta el modificador parcial en la declaración de tipo ‘Numalet’;
ya existe otra declaración parcial de este tipo
Hola, alguien me podría ayudar en la duda que tengo porfavor, lo que pasa que es la primera vez que trabajo con clases en C#, entonces nose como llamarla o hacerla funcionar para ver el resultado en la interfaz gráfica.
Gracias.
EN primer lugar felicitaciones por toda tu ayuda para con los demás…
Disculpa la molestia, estoy haciendo un forma para mostrar de numeros a letras, un textBox donde ingreso el numero, y una buttonMostrar para mostrarme ya sea en Label o en otro TextBox… pero nosé como asociarlo…
Soy nuevo en esto… apenas estoy comenzando, agradecería tu ayuda.. 🙂
Muchas gracias!!!
Me sera muy util
Hola amigo, desde hace un tiempo he estado haciendo una recopilación de lo que yo considero librerías útiles (propias y de otros programadoras) y con tu permiso me gustaría agregar la tuya, mi idea es publicarla en el momento que ya tenga una cantidad razonable de clases añadidas. Aunque la liberaré como archivo .dll añadiré el código fuente para que cualquiera pueda mejorarla o adaptarla como mejor le parezca.
Espero tu respuesta y gracias de antemano 🙂
Hola rdaw. No hace falta ni que pidas permiso, solo que que cites la fuente aunque sea en el código fuente por mi está bien. ¡Gracias y saludos!
Gracias por tu respuesta, y por tu confirmación y no te preocupes todos los códigos tienen los nombres de los autores en los comentarios, así como la página de donde fueron pedidos, me llevará aún algo de tiempo, pero al finalizar enviaré la dll y el código a todas estas personas 😀
Saludos!
olas ps disculpa ps soi un estudiante ps nose si me pueden ayudar a prender a programar n tendran unos manuales o unas paginas q t me enseñen desde cero 😀 les agreseria mucho
olas ps disculpa ps soi un estudiante ps nose si me pueden ayudar a prender a programar n tendran unos manuales o unas paginas q t me enseñen desde cero les agreseria mucho
hola que tal amigo
queria darte las gracias por compartir esta funcion.
la verdad esque yo no soy para nada programador pero ahi a base de puros golpes y tamborasos ya la pude implementar y me sirvo mucho.
sigue asi amigo
saludos
Hola Alejandro, primero que nada agradecerte por tu gran trabajo y felicitarte por lo mismo, creo que a estas alturas tu clase es ya un estándar 😀
También quiero hacer una pequeña observación, y es que al querer convertir un número decimal con el método ToCustomCardinal, el resultado no fue el deseado, por lo que inspeccioné el código y encontré un pequeño error. En la línea 219 esta el siguiente código:
por lo que basándome en las otras sobrecargas cambié dicha línea por:
y Ahora funciona correctamente con un número decimal.
Mil gracias nuevamente
Salu2
Buen trabajo, como dijeron antes, me ahorraste tiempo de programación, te felicito.
alguien sabe este codigo en php??
Excelente Código, Muchas Gracias
Muchas gracias 🙂
MUchas muachas gracias, aun sigo agradecido por tu codigo, creo que he tardado mas en llegar al ultimo de esta pagina que en lo que realizaba las pruebas, estoy muy agradeciddo me ahorraste 1 dia de trabajo y mucho pensar gracias sigue asi. y felicidades
me sucede lo siguinte estoy usando el algoritmo para los cheques del banco
y el banco cunado convierto el numero 6000.00 el algoritmo lo pone asi «seis mil con 0/100 y lo necesito asi «seis mil con 00/100» o se antes del caracter / necesito dos digitos.Otros ejmplo si convierto 6000.04 me pone el algoritmo «seis mil con 4/100» y necesito seis mil con 04/100.hay alguna propiedad que haga eso.Muchas gracias agradeceria su ayuda
la clase que nos da nuestro amigo es con valores double, si usas decimales o numericos no te servirá, tendrias que pasarle valores double
Excelente!!!
Me salvaste el fin de semana!!!
Muy buen trabajo, tengo una pregunta, si fuera el caso del número 001, y quiero que lo convierta a «cero cero uno», esto se puede? que convierta los ceros. Otro ejemplo 00125, que convierta a «cero cero ciento veinticinco». Alguien puede ayudarme
Muchas Gracias, saludos desde Honduras.
Gracias men te debo una, me sacaste del apuro,
gracias por el codigo, iba a la cuarta parte de hacer algo parecido y casi muero de migrania, y entonces encontre esto ;D
Muchas Gracias Me Sirvio Demasiado 😉
Buen dia este codigo es justo lo que necesito para terminar un programa de facturacion pequeño que estoy haciendo, el enlace ya esta roto 😦 podria por favor volverlo a subir se lo agradeceria mucho
saludos 😀
Sawaa, ya está reemplazado el link. Si hay algún problema me avisás. Gracias.
Muchas Gracias Alejandro por tu gran aportate al publico, sabiendo del tiempo que siempre toma el estar sentado programando y que no toda la gente valora o aprecia este tipo de cosas. Saludos desde Mexico.
Excelente trabajo. Felicidades
Hola.. muy bueno..
la verdad te felicito doc. es muy bueno y bien documentado.
pero sabes una observacion? en la RAE
1000 = mil (también, como sust., un millar)
pero cuando se escribe en el programa sale «un mil»
me di cuenta cuando quería imprimir 1000 en letras jeje
no le he dado solución aun.
Jimmy: es correcto que el código no contempla el uso de «millar». Entiendo que este es correcto como sustantivo aunque en general para adjetivar, («un millar de personas»). Pero su uso creo es más acorde con cantidades indeterminadas «Varios millares», «Llegaban de a millares» y está fuera del objetivo del código.
Sin embargo me suena correcto cuando no se llega a los cientos de miles, sería incorrecto pronunciar 201.400 como «doscientos un millar cuatrocientos», por lo que analizando el número entrante podrías reemplazar la salida facilmente si se encuentra entre 1000 para «millar» y 2000 a 99.999 para «millares» (noventa y nueve millares novecientos noventa y nueve).
Aun así, debería ser un caso muy específico, al menos para los usos que conozco.
Saludos y gracias por el comentario.
Hola, muchas gracias por este ejemplo, es lo que estaba buscando, lo probé y funciono a la primera ejecución, nuevamente gracias por tu aporte.
Hola, muchas gracias por tu aporte, lo use y a la primera ejecución funciono, saludos.
Aquí una librería gratuita que hace todo esto y mas!!! Here is a free library that does all this and more !!!
http://urysoft.blogspot.com.es/p/urysoftutilities.html?view=sidebar
ni hablar esta Clase es de Maravilla Alejandro aun después de muchas versiones del vs sigue sirviendo de mucho Buen Trabajo Tio
No manejo vb solo vba, pero aun así está muy bien explicado.
Hola buen día, mil gracias por compartir tu código, muy bien explicado y el resultado es excelente.
Excelente trabajo, te felicito; solo tengo una duda, como puedo dar formato para que cuando sea un digito los centavos los convierta a dos, por ejemplo
2.01 resultado = dos con 1/100
Y necesito:
2.01 resultado = dos con 01/100
Mil gracias.
Muy buena, gracias campeón!
Este código me ha ayudado una infinidad de veces, mi mas sincero agradecimiento a su generoso autor.
la acabo de implementar y simplemente genial, mis mas sincero agradecimiento
Muchisimas gracias, funciona muy bien, casualmente encotre un bug si mandas por ejemplo 10.66.
La funcion Convertir marca error: Decimal byte array constructor requires an array of length four containing valid decimal bytes.
Lo que se peude evittar simplemente cambiando ByVal Numero As Decimal por ByVal Numero As Double
Saludos!
Buenas amigo, un poco tarde pero al fin consegui este código, mi consulta es como realizo la instalación de lo que se descarga, soy nuevo en programación pero con un poco de conocimiento y estoy realizando un programa para mi empresa y necesito reflejar en el presupuesto la cantidad expresada en letras y este código iria excelente, mas no se como instalarla para que el VB lo reconosca, trabajo con VB6. Gracias de antemano
Es imposible que VB6 pueda hacer correr un proyecto de .NET, necesitas por fuerza programarlo a mano
Funcionada demasiado bien !! de verdad muchas gracias
Gracias por el aporte lo estoy probando.
Muy buen codigo. Imaginate, lo creastes en el 2007 y hasta ahora se sigue usando, y mas aun con buenos comentarios. Muchas Gracias por compartir.
hola en C# me pregunto como puedo hacer un programa que diga según la siguiente tabla cuantas horas necesitas dormir.
a. o-3 meses: entre 14 a 17 horas
b. 4-11 meses: 12 a 15 horas
c. 1-2 años : entre 11 a 14 horas
d. 3-5 años: 10 a 13 horas
e. 6-13 años: 9 a 11 horas
f. 14-17 años: 10 horas
g 18-25 años: 7 a 9 horas, no menos de 6 y no mas de 11
h. 26-64 años: 7 a 9 horas
i. adultos mayores de 65 años: 7 a 8 horas
Gracias! muy útil
SUPER BUENO!!!! YA ME IBA A DAR POR VENCIDO AUNQUE IGUAL TENGO QUE VER COMO LO AGREGO A UN REPORTE.
Mis respetos Maestro, super vacano todo, le hice unos cambios de forma que le estare mostrando.
Sencillamente Genial.
Lo felicito desde Barranquilla Colombia.
Muchas gracias por compartir el código amigo me ahorró bastante tiempo de trabajo, saludos!
Como puedo utilizar este codigo en windows form ¿? Con diseño
Muchas gracias por el código. Me es muy útil!!!
Muchísimas gracias por el código. Me anduvo de una. Excelente. Muy agradecido por tu colaboración.
Todos deberian publicar codigos asi un abrazo
En este video aprendí como hacerlo, en la descripcion se puede descargar el proyecto , por si gustan checkearlo.
link: https://www.youtube.com/watch?v=GThaZPLJq_k&t=17s este link es en C#, si lo desean en Vb.net el link sería el siguiente: https://www.youtube.com/watch?v=HUXNFe0LkG8