SlideShare una empresa de Scribd logo
1 de 12
Descargar para leer sin conexión
RUTINA PARA CONVERTIR UNA CANTIDAD NUMÉRICA EN
                          LETRAS.


 MOTIVACIÓN.
En algún momento, mientras navegábamos en Internet o mientras analizábamos el código que
“descubrimos” en el trabajo de nuestro amigo Nerdio, nos encontramos con algún
script/función/clase que convertía cadenas numéricas a su equivalente en palabras (en letras,
dirían algunos). Seguramente en ese momento ustedes pensaron lo mismo que yo: “¿para qué me
puede servir eso? Nunca voy a escribir un cheque usando PHP        ...”. Y llegados a esa conclusión
simplemente seguimos a otra página web, borramos esa parte del código o la imprimimos en un
papel que nunca más encontramos.
Y entonces llega el día en que el jefe abre la boca y salen estas palabras: “Quiero que los cheques
(facturas, recibos, etc.) las haga el sistema” (Así le dicen siempre, “el sistema”). Claro que es
más fácil ponerlo a uno a codificar que decirle a la secre que se salga de facebook y escriba un
cheque en la bendita máquina de escribir. Y es entonces cuando buscamos, rebuscamos y no
encontramos la dichosa rutina.
Así que llegamos al problema: ¿y ahora cómo diablos hago para que mi número salga convertido
en letras?
Se encuentran algunas funciones para eso en Internet (personalmente probé varias) pero como
sucede con todo código que no es nuestro, si queremos modificar algo primero tenemos que
entender cómo funciona y lo más probable es que pasemos varias horas sin entender nada. Y a
menos que esté bien que nuestros cheques digan algo así como “двести пятьдесят рублей”,
tenemos que modificar algo.
Así que me dediqué a elaborar mi propia rutina y, siendo de buen corazón, ahora quiero
compartirla con ustedes, pero no solamente quiero entregarles el código sino también el
razonamiento detrás del código. No pretendo que sea la rutina mejor pensada ni la mejor
codificada. Estoy seguro de que hay margen para perfeccionarla y al menos cinco formas de
hacerla mejor, pero, parafraseando el dicho: “no hay código por malo que sea que no nos deje
algo bueno”. Como siempre, los comentarios son bien recibidos.


 PARA LOS IMPACIENTES
¿No te interesa nada de lo que tengo que decir? Ve directamente a la sección EL SCRIPT,
COMPLETO, allí está todo el código.
Adaptaciones que hay que hacer: en la función num2letras() hay que cambiar el nombre de la
moneda (la variable $moneda) y arreglar el plural al final de la función. También hay que tener
en cuenta que pueden introducirse comillas tipográficas (“”) al hacer copy/paste.
¿La forma de uso?: ver la sección “¿Y CÓMO USO TODO ESO?”.
Mis disculpas por hacerte trabajar.


ANALISIS DEL SCRIPT


ALGUNOS DATOS PREVIOS
  1. Este script maneja números de hasta 15 cifras (más dos decimales), o sea, llega hasta 999
     billones y centavos. Esto es así debido a problemas de precisión que surgen al trabajar
     con números grandes.
  2. En mi país, Guatemala, utilizamos como moneda el Quetzal. Decidí poner el nombre de la
     moneda en una variable al principio del código porque me imagino que, si estás leyendo
     esto, es bastante probable que tengas que cambiarlo (a menos que seas de Guatemala,
claro). La forma del plural se arregla al final del script, cuando ya sabemos exactamente
      con qué cantidad estamos tratando.
   3. Utilizamos dos funciones: enletras() y num2letras(). La primera convierte un número de
      tres cifras en su equivalente en palabras. La segunda función se apoya en la primera:
      separa un número de hasta 15 dígitos en grupos de tres cifras y pasa cada uno de esos
      grupos a la primera función para su procesamiento. Por último, esta segunda función da
      formato a la cadena de palabras obtenida para su presentación correcta.



 LA FUNCIÓN ENLETRAS()
La función enletras() toma un solo parámetro: una cadena de caracteres 1 que representa un
número de hasta tres cifras y que se pasa desde la función num2letras() y devuelve ese número
convertido en palabras:

function enletras($valor) {
   -- código php
   return $en_letras;
}

Básicamente, en español2, podemos “decir” cualquier número usando grupos de tres dígitos y
concatenando esos grupos con calificadores del nivel. Así por ejemplo, el número 11,234,567 será
“once millones doscientos treinta y cuatro mil quinientos sesenta y siete”, donde los calificadores
son las palabras “millones” y “mil”, que concatenan los tres grupos “once”, “doscientos treinta y
cuatro” y “quinientos sesenta y siete”.
Teniendo lo anterior en cuenta, podemos crear tres matrices (una para las unidades, otra para las
decenas y una tercera para las centenas) que nos permiten formar el nombre de cualquier grupo
de tres dígitos. Hay que tener en cuenta que los números 11 al 19 tienen su propio nombre,
mientras que los otros números utilizan una combinación de nombres más sencillos (por ejemplo,
43 = “cuarenta y tres”). Las matrices serán:

$uni[0] = "cero";
$uni[1] = "un";
$uni[2] = "dos";
$uni[3] = "tres";
$uni[4] = "cuatro";
$uni[5] = "cinco";
$uni[6] = "seis";
$uni[7] = "siete";
$uni[8] = "ocho";
$uni[9] = "nueve";
$uni[11] = "once";
$uni[12] = "doce";
$uni[13] = "trece";
$uni[14] = "catorce";
$uni[15] = "quince";
$uni[16] = "dieciseis";
$uni[17] = "diecisiete";
$uni[18] = "dieciocho";
$uni[19] = "diecinueve";

$dec[2] = "veinti";
$dec[3] = "treinta y ";
1 No es lo mismo 'una cadena de caracteres que representa un número' que 'un número'. Ver la
  documentación de PHP: http://www.php.net/manual/es/language.types.php

2 Castellano, para los puristas.
$dec[4] = "cuarenta y ";
$dec[5] = "cincuenta y ";
$dec[6] = "sesenta y ";
$dec[7] = "setenta y ";
$dec[8] = "ochenta y ";
$dec[9] = "noventa y ";
$dec[10] = "diez";
$dec[20] = "veinte";
$dec[30] = "treinta";
$dec[40] = 'cuarenta';
$dec[50] = 'cincuenta';
$dec[60] = 'sesenta';
$dec[70] = 'setenta';
$dec[80] = 'ochenta';
$dec[90] = 'noventa';

$cen[1] =   "ciento ";
$cen[2] =   "doscientos ";
$cen[3] =   "trescientos ";
$cen[4] =   "cuatrocientos ";
$cen[5] =   "quinientos ";
$cen[6] =   "seiscientos ";
$cen[7] =   "setecientos ";
$cen[8] =   "ochocientos ";
$cen[9] =   "novecientos ";
$cen[100]   = "cien";
$cen[200]   = "doscientos";
$cen[300]   = "trescientos";
$cen[400]   = "cuatrocientos";
$cen[500]   = "quinientos";
$cen[600]   = "seiscientos";
$cen[700]   = "setecientos";
$cen[800]   = "ochocientos";
$cen[900]   = "novecientos";

Como primer paso, determinamos el largo de la cadena que la función recibió como parámetro.
Este largo nos dice cuántos dígitos tiene el grupo y, según sea el valor obtenido, así lo
procesaremos.

$largo = strlen($valor);

La variable $largo puede tomar solamente tres valores: 1, 2 ó 3. Debemos recordar que una
cadena de caracteres también es una matriz y cada uno de sus elementos puede identificarse con
un índice. Por ejemplo, para la cadena $var = 'ABC', sus elementos serán $var[0] = 'A', $var[1] =
'B' y $var[2] = 'C'. De la misma manera, podemos usar $valor[0], $valor[1] y $valor[2] para
identificar cada uno de los dígitos del parámetro $valor que se ha pasado a la función.
Usaremos un lazo switch() para decidir cómo procesaremos el parámetro $valor.

switch($largo) {
   case 1:
       $en_letras = $uni[$valor[0]];
       break;

El primer caso es el más sencillo. Hemos recibido una cadena que contiene un solo caracter: un
dígito cuyo valor se encuentra entre 1 y 9. Para este caso, simplemente buscamos el valor
correspondiente en la matriz $uni[] y lo asignamos a la variable $en_letras.

   case 2:
$decena = $valor[0] . $valor[1];
       if($valor[1] == 0) {
          // decena exacta
          $en_letras = $dec[$decena];
       } else {
          if($valor[0] > 1) {
              // entre 21 y 99
              $en_letras = $dec[$valor[0]] . $uni[$valor[1]];
          } else {
              // entre 11 y 19
              $en_letras = $uni[$decena];
          }
       }
       break;

El segundo caso trata con cadenas de dos caracteres: un número de dos dígitos cuyo valor se
encuentra entre 11 y 99. Aquí pueden darse dos situaciones diferentes: que tengamos una
decena exacta (10, 20, 30, etc.) o que no lo sea.
Si el valor del último dígito ($valor[1]) es cero, la decena será exacta y buscamos su “nombre” en
la matriz $dec[]. Para ello usamos la variable $decena, formada por los dígitos $valor[0] y
$valor[1].
Si no se trata de una decena exacta (por ejemplo: 34, 57, 89, etc.), aún tenemos dos opciones que
considerar: que se trate de números entre 11 y 19, en cuyo caso buscamos su nombre en la
matriz $uni[], o que se trate de un número mayor. En este último caso concatenamos el nombre
de la decena obtenido de la matriz $dec[] con el nombre de la unidad obtenido de la matriz
$uni[].

   case 3:
      // centenas, decenas y unidades
      $centena = $valor[0] . $valor[1] . $valor[2];
      if($centena != '000') {
          $decena = $valor[1] . $valor[2];
          if($valor[1] == 0 AND $valor[2] == 0) {
             // centena exacta
             $en_letras = $cen[$centena];
          } else {
             if($valor[2] == 0) {
                 // decena exacta
                 $en_letras = $dec[$decena];
             } else {
                 if($valor[1] == 0 AND $valor[2] == 1) {
                    // exactamente 1
                    $en_letras = $uni[$valor[2]];
                 } elseif($valor[1] > 1) {
                    // entre 21 y 99
                    $en_letras = $dec[$valor[1]] . $uni[$valor[2]];
                 } else {
                    // entre 11 y 19
                    $en_letras = $uni[$decena];
                 }
             }
             $en_letras = $cen[$valor[0]] . $en_letras;
          }
      } else {
          $en_letras = "000";
      }
      break;
   }
Veamos ahora el último caso: una cadena que representa un número de tres dígitos (entre 100 y
999).
Comprobamos que la cadena no sea igual a '000' (por ejemplo, si tenemos originalmente un
número como 1,000 y estamos procesando el grupo de la derecha). Si la cadena es '000',
simplemente devolvemos esa misma cadena para manejarla en la función num2letras(), como se
verá más adelante.
Ahora veamos las decenas. Si los dígitos $valor[1] y $valor[2] son iguales a cero, tenemos una
centena exacta (100, 200, 300, etc.). En este caso buscamos el nombre directamente en la matriz
$cen[].
Como en el caso anterior del switch(), revisamos si estamos tratando con una decena exacta. Si
es así, obtenemos su nombre directamente de la matriz $dec[].
También debemos comprobar otra situación especial: el segundo dígito es cero y el tercero es '1'
(101, 201, 301, etc.) En este caso asignamos temporalmente a la variable $en_letras el valor
'UN'. Y decimos que es temporalmente porque un poco más abajo se decide si conservamos este
valor o no.
Por último y como en el segundo caso del switch() (la cadena de dos números), si los últimos dos
dígitos se encuentran en el rango entre 11 y 99, buscamos su nombre en la matriz $uni[]. Si se
encuentran entre 21 y 99 (el número 20 ya se trato antes como una decena exacta), su nombre
será la combinación de el nombre de la decena en la matriz $dec[] y la unidad en la matriz $uni[].
Ahora obtenemos el nombre de la centena de la matriz $cen[] y le agregamos el valor de la
variable $en_letras (que contiene el nombre de los dos dígitos restantes).
Una vez que llegamos aquí, la variable $en_letras contiene el “nombre” del grupo de tres dígitos
que se pasó como parámetro y solo nos queda devolverlo.

return $en_letras;



 LA FUNCIÓN NUM2LETRAS()
Esta función se ocupa básicamente de darle un formato adecuado para su impresión al valor
devuelto por la función $enletras() .Toma como parámetro un número que será el que queremos
convertir de dígitos a “palabras”.

function num2letras($val) {
   -- código php
}

Definimos una variable que contendrá el nombre de la moneda. Uso una variable porque es más
sencillo de adaptar. En mi país, Guatemala, usamos el Quetzal como moneda.

$moneda = 'QUETZAL';

Como el número que se pasará a esta función puede venir “contaminado” con espacios, comas,
símbolos de moneda, etc., debemos limpiarlo para quedarnos solamente con dígitos y, tal vez, un
punto decimal. La función eregi_replace() toma como patrón las letras A-Z (mayúsculas o
minúsculas) y el símbolo '$'. Eso cubre casi todos los símbolos de monedas de los países
hispanoparlantes, pero si estas en un país que usa otro símbolo (por ejemplo, el yen), éste se
agrega aquí.

$num = eregi_replace("[A-Za-z$, ]", "", $val);

Hay que convertir nuestro número a número. No, todavía no estoy desvariando. Suena confuso,
pero hasta ahora hemos trabajado con cadenas de caracteres que representan números, no con
números verdaderos. Sin embargo, necesitamos que nuestro número lo sea realmente, para darle
formato con dos decimales y los miles separados por comas. ¿Para qué ponerle comas después de
que se las quitamos? La importancia de esto se verá más abajo.
$num = (float) $num;

Como dijimos, necesitamos un número. Además, al convertir nuestra cadena usando float(), nos
aseguramos de desaparecer los ceros a la izquierda.

$num = number_format($num, 2, ".", ",");

Si anteriormente no no podíamos estar seguros de cómo estaba formado nuestro número, en este
momento ya tenemos con seguridad un número con un punto y dos decimales. Lo separamos en
dos partes usando el punto decimal como referencia. Por ahora solo manejaremos las cantidades
enteras y nos ocuparemos de los decimales (los centavos) al final del script.

$enteros = substr($num, 0, -3);

Una vez que sabemos cuál es el valor de la parte entera, lo descomponemos en grupos de tres
dígitos usando las comas que introdujimos anteriormente (les dije que eran importantes). Si
nuestro número originalmente traía comas, no podíamos confiar en que estaban en el lugar
correcto, así que lo que hicimos fue asegurarnos de que el número tuviera el formato
exactamente como lo necesitamos.

$trios = explode(",", $enteros);

Ahora algunas inicializaciones necesarias: creamos la matriz $partes() que contendrá como
elementos los “nombres” que vamos obteniendo de la función enletras(); un contador $n = 0, que
será el índice de cada elemento que se agregue a esa matriz; y una variable vacía, $en_letras, en
la que se formará el “nombre” de nuestro número.

$partes = array();
$n = 0;
$en_letras = '';

Ahora recorremos cada grupo de números. Como dijimos al principio, por asuntos relacionados
con la precisión, solamente manejaremos hasta 15 dígitos. Si cada grupo contiene tres dígitos (o
menos), debemos hacer el siguiente procedimiento un máximo de cinco veces.

for($i = 5; $i >= 0; $i--) {

Primero comprobamos la longitud del grupo, para saber si en realidad existe. Asumimos que
tenemos 15 dígitos, pero esto puede no ser verdad.

   $largo = strlen($trios[$i]);

Si el grupo existe ($largo tiene un valor), vamos a procesar el grupo. Más arriba habíamos
dividido el número en grupos (almacenados en la matriz $trios[]). Como nuestro lazo inicia con $i
= 5 y va disminuyendo, quiere decir que el primer grupo que enviaremos a la función enletras()
será $trios[5], que es el trío de la extrema derecha (el de menor nivel) y a partir de allí nos
moveremos hacia la izquierda hasta llegar al trío de la extrema izquierda, el de mayor nivel.
La función enletras() procesa cada trío y, como ya vimos antes, devuelve su valor expresado en
palabras. Ese valor se agrega como un nuevo elemento (iniciando con el índice $n = 0 y
aumentando de valor) a la matriz $partes[]. Ese proceso se repite para cada iteración del lazo
for().

   if($largo) {
      $partes[$n] = enletras($trios[$i]);

En cada iteración también analizamos el contenido del elemento $partes[$n] que acabamos de
agregar.
Veamos el primer elemento (número <= 999). Como ya dijimos antes, si se da el caso de que el
grupo este formado solamente por ceros (por ejemplo, el grupo de la derecha en el número
1,000), la función devuelve esa misma cadena ('000') y la variable $en_letras toma el valor “DE“.
Si el número no es 000, $en_letras toma el valor contenido en el elemento $partes[0].

       if($partes[0] != '000') {
          $en_letras = $partes[0];
       } else {
          $en_letras = ' DE ';
       }

Analicemos el segundo grupo desde la derecha, el de los miles. Si está vacío (nuestro número
solo tenía tres dígitos), simplemente continuamos hasta el final del lazo para otra iteración. Si el
valor de la variable $en_letras es solamente “DE“, vaciamos esa variable (por ejemplo, si nuestro
número es 1,000 no se dice “un mil de quetzales”, sino “un mil quetzales”. La palabra “de”
sobra). Si $en_letras tiene otro valor (por ejemplo: “trescientos setenta y uno”), concatenamos el
valor devuelto por la función enletras() para este trío con el ya existente en la variable
$en_letras, para formar un nombre que contiene miles, por ejemplo: “dos mil trescientos setenta
y uno”.
        if($partes[1] != '') {
            if($en_letras == ' DE ') {
               $en_letras = '';
            }
            $en_letras = $partes[1] . " MIL " . $en_letras;
        }

Para el tercer grupo desde la derecha, el de los millones, el procedimiento es similar. Si existe, lo
procesamos, si no, continuamos hasta el final del lazo. Como nuestro calificador puede ser
“millón” o “millones”, primero decidimos cuál usaremos según sea el valor contenido en
$partes[2]. Si $partes[1] y $partes[0] (los dos grupos de la derecha) son diferentes de '000',
asignamos a la variable $en_letras todos esos valores (por ejemplo: cinco millones dos mil
trescientos setenta y uno). En el caso de que $partes[1] y $partes[0] sean ambas iguales a '000'
(por ejemplo: 1,000,000), la concatenación será “un millón de”.

       if($partes[2] != '') {
          $millones = " MILLONES ";
          if(strtoupper($partes[2]) == 'UN') {
              $millones = " MILLON ";
          }
          if($partes[0] != '000' AND $partes[1] != '000') {
              $en_letras = $partes[2] . $millones . $en_letras;
          } else {
              $en_letras = $en_letras = $partes[2] . $millones . ' DE ';
          }

       }

El cuarto grupo (miles de millones) sigue un análisis similar. Si este grupo existe y los otros
grupos no contienen únicamente ceros, concatenamos esos valores. Siguiendo nuestro ejemplo,
$en_letras contiene en este momento el valor “cinco millones dos mil trescientos setenta y uno” y
luego de procesar este grupo contendrá “ochenta mil cinco millones dos mil trescientos setenta y
uno”. Si los otros grupos todos contienen '000' (1,000,000,000), $en_letras será igual a “un mil
millones de”.

       if($partes[3] != '') {
          if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000') {
              $en_letras = $partes[3] . " MIL " . $en_letras;
          } else {
$en_letras = $partes[3] . " MIL MILLONES DE ";
            }
        }

Finalmente, el quinto grupo, el de la extrema izquierda. El análisis es también parecido. Primero
decidimos qué calificador utilizaremos: “billón” o “billones”. Luego, si los restantes grupos tienen
un valor, concatenamos esos valores y los asignamos a la variable $en_letras. En caso contrario
(todos los otros grupos contienen '000'), nuestro nombre será “1 billón de”.

       if($partes[4] != '') {
          $billones = " BILLONES ";
          if(strtoupper($partes[4]) == 'UN') {
              $billones = " BILLON ";
          }
          if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000' AND
$partes[3] != '000') {
              $en_letras = $partes[4] . $billones . $en_letras;
          } else {
              $en_letras = $partes[4] . $billones . ' DE ';
          }

        }
        $n++;
    }
}

Ahora debemos ocuparnos del plural de la moneda. Como ya indiqué, en mi país la moneda
nacional es el Quetzal. Plural: quetzales (se agrega 'es'). La regla es sencilla: si el nombre de la
moneda termina en vocal, se agrega una 's' (peso → pesos, lempira → lempiras, etc.) en otro caso,
se agrega 'es' (quetzal → quetzales, bolivar → bolívares, etc.)

if(strtoupper($en_letras) != 'UN') {
   $moneda = " " . $moneda . "ES";
}

Vamos a ocuparnos de los centavos. La presentación que usamos aquí es 'XX/100', donde XX
representa la cantidad en centavos (por ejemplo: 35/100). También podría usarse 'XX centavos' o,
en el caso de que sean cero centavos decir 'tantos quetzales exactos'. Eso queda al gusto del
contador de la empresa o de la persona a cargo de los cheques/recibos/facturas o lo que sea. En
cualquier caso, la siguiente parte del script puede modificarse fácilmente.

$centavos = substr($num, -2) . "/100";

Por último, lo ponemos todo junto y devolvemos nuestro resultado.

$letras = strtoupper($en_letras) . $moneda . " CON " .       $centavos;
return $letras;


 ¿Y CÓMO USO TODO ESO?
En la práctica, en este momento ya debemos tener una cantidad disponible. Ya sea porque la
calculamos, porque viene de un formulario mediante $_POST[], estaba almacenada en una tabla
de nuestra base de datos o simplemente existe en alguna forma. Cuando llega el momento de
imprimir esa cantidad, simplemente hacemos la llamada:

$echo num2letras($total_en_numero);
CONCLUSIONES
Como dije antes, seguramente hay una forma mejor y más eficiente de codificar todo esto.
Pueden modificar este script a su gusto, no hay problema con eso. Si han aprendido algo al leer
este trabajo, me doy por satisfecho. Sus comentarios, críticas y sugerencias son siempre
bienvenidos, sobre todo si se refiere a errores de código o tipográficos.
Y, como siempre, recuerden que usan este script por su cuenta y riesgo. Funciona como debe de
funcionar y hace lo que dice que hace (y nada más), pero si algo les pasa (se les calienta la
cerveza, los deja la novia/el novio, les da gripe, los despiden del trabajo, reprueban el exámen,
etc.), no me culpen a mí.

Juan Carlos Vásquez
jcvasquez07@gmail.com


EL SCRIPT, COMPLETO
Recuerden:
   – Hay que adaptar la variable $moneda y el plural al final de la función num2letras().
   – Al hacer copy/paste pueden introducirse comillas tipográficas (“”). Hay que sustituirlas
      por comillas normales en su editor preferido.

function enletras($parte) {
     // toma como parámetro un numero entero de hasta tres cifras
     $uni[0] = "cero";
     $uni[1] = "un";
     $uni[2] = "dos";
     $uni[3] = "tres";
     $uni[4] = "cuatro";
     $uni[5] = "cinco";
     $uni[6] = "seis";
     $uni[7] = "siete";
     $uni[8] = "ocho";
     $uni[9] = "nueve";
     $uni[11] = "once";
     $uni[12] = "doce";
     $uni[13] = "trece";
     $uni[14] = "catorce";
     $uni[15] = "quince";
     $uni[16] = "dieciseis";
     $uni[17] = "diecisiete";
     $uni[18] = "dieciocho";
     $uni[19] = "diecinueve";

    $dec[2] = "veinti";
    $dec[3] = "treinta y ";
    $dec[4] = "cuarenta y ";
    $dec[5] = "cincuenta y ";
    $dec[6] = "sesenta y ";
    $dec[7] = "setenta y ";
    $dec[8] = "ochenta y ";
    $dec[9] = "noventa y ";
    $dec[10] = "diez";
    $dec[20] = "veinte";
    $dec[30] = "treinta";
    $dec[40] = 'cuarenta';
    $dec[50] = 'cincuenta';
    $dec[60] = 'sesenta';
$dec[70] = 'setenta';
$dec[80] = 'ochenta';
$dec[90] = 'noventa';

$cen[1] = "ciento ";
$cen[2] = "doscientos ";
$cen[3] = "trescientos ";
$cen[4] = "cuatrocientos ";
$cen[5] = "quinientos ";
$cen[6] = "seiscientos ";
$cen[7] = "setecientos ";
$cen[8] = "ochocientos ";
$cen[9] = "novecientos ";
$cen[100] = "cien";
$cen[200] = "doscientos";
$cen[300] = "trescientos";
$cen[400] = "cuatrocientos";
$cen[500] = "quinientos";
$cen[600] = "seiscientos";
$cen[700] = "setecientos";
$cen[800] = "ochocientos";
$cen[900] = "novecientos";

$largo = strlen($parte);
// Segun el largo del numero, tenemos unidades
// decenas y centenas
switch($largo) {
      case 1:
          // solo unidades
          $en_letras = $uni[$parte[0]];
          break;
      case 2:
          // decenas y unidades
          $decena = $parte[0] . $parte[1];
          if($parte[1] == 0) {
                // decena exacta
                $en_letras = $dec[$decena];
          } else {
                if($parte[0] > 1) {
                      // entre 21 y 99
                      $en_letras = $dec[$parte[0]] . $uni[$parte[1]];
                } else {
                      // entre 11 y 19
                      $en_letras = $uni[$decena];
                }
          }
          break;
      case 3:
          // centenas, decenas y unidades
          $centena = $parte[0] . $parte[1] . $parte[2];
          if($centena != '000') {
                $decena = $parte[1] . $parte[2];
                if($parte[1] == 0 AND $parte[2] == 0) {
                      // centena exacta
                      $en_letras = $cen[$centena];
                } else {
                      if($parte[2] == 0) {
                            // decena exacta
                            $en_letras = $dec[$decena];
} else {
                                 if($parte[1] == 0 AND $parte[2] == 1) {
                                       // exactamente 1
                                       $en_letras = $uni[$parte[2]];
                                 } elseif($parte[1] > 1) {
                                       // entre 21 y 99
                                       $en_letras = $dec[$parte[1]] . $uni[$parte[2]];
                                 } else {
                                       // entre 11 y 19
                                       $en_letras = $uni[$decena];
                                 }
                           }
                           $en_letras = $cen[$parte[0]] . $en_letras;
                      }
                } else {
                      $en_letras = "000";
                }

               break;
          }
          return $en_letras;
}

function num2letras($val) {
     $moneda = 'QUETZAL';
     $num = eregi_replace("[A-Za-z, ]", "", $val);
     $num = (float) $num;
     $num = number_format($num, 2, ".", ",");

     // Solo manejamos los enteros. Los centavos los agregamos al final
     $enteros = substr($num, 0, -3);
     $trios = explode(",", $enteros);

     $partes = array();
     $n = 0;
     $en_letras = '';
     for($i = 5; $i >= 0; $i--) {
           $largo = strlen($trios[$i]);
           if($largo) {
                 // aqui procesamos las partes
                 $partes[$n] = enletras($trios[$i]);
                 if($partes[0] != '000') {
                       $en_letras = $partes[0];
                 } else {
                       $en_letras = ' DE ';
                 }

                if($partes[1] != '') {
                      if($en_letras == ' DE ') {
                            $en_letras = '';
                      }
                      $en_letras = $partes[1] . " MIL " . $en_letras;
                }

                if($partes[2] != '') {
                      $millones = " MILLONES ";
                      if(strtoupper($partes[2]) == 'UN') {
                             $millones = " MILLON ";
                      }
if($partes[0] != '000' AND $partes[1] != '000') {
                         $en_letras = $partes[2] . $millones . $en_letras;
                   } else {
                         $en_letras = $en_letras = $partes[2] . $millones . ' DE ';
                   }

              }

              if($partes[3] != '') {
                    $en_letras = $partes[3] . " MIL " . $en_letras;
                    if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000') {
                          $en_letras = $partes[3] . " MIL " . $en_letras;
                    } else {
                          $en_letras = $partes[3] . " MIL MILLONES DE ";
                    }
              }

              if($partes[4] != '') {
                    $billones = " BILLONES ";
                    if(strtoupper($partes[4]) == 'UN') {
                           $billones = " BILLON ";
                    }
                    if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000' AND $partes[3] != '000') {
                           $en_letras = $partes[4] . $billones . $en_letras;
                    } else {
                           $en_letras = $partes[4] . $billones . ' DE ';
                    }

              }
              $n++;
         }
    }

    if(strtoupper($en_letras) != 'UN') {
           $moneda = " " . $moneda . "ES";
    }

    // Los centavos
    $centavos = substr($num, -2) . "/100";

    $letras = strtoupper($en_letras) . $moneda . " CON " . $centavos;
    return $letras;
}

Más contenido relacionado

La actualidad más candente

Lenguaje de Programacion - Java
Lenguaje de Programacion - JavaLenguaje de Programacion - Java
Lenguaje de Programacion - Javazousbabastre
 
Método de creación de contraseñas robustas de thi4 go
Método de creación de contraseñas robustas de thi4 goMétodo de creación de contraseñas robustas de thi4 go
Método de creación de contraseñas robustas de thi4 goVerónica Meo Laos
 
Problemas propuestos2.0
Problemas propuestos2.0Problemas propuestos2.0
Problemas propuestos2.0YO Por Que
 
63 Php. Imagenes Con Lineas Y Textos
63 Php. Imagenes Con Lineas Y Textos63 Php. Imagenes Con Lineas Y Textos
63 Php. Imagenes Con Lineas Y TextosJosé M. Padilla
 
Arreglos en c ++
Arreglos en c ++Arreglos en c ++
Arreglos en c ++tacubomx
 
80 Php. Campos Y Conexiones
80 Php. Campos Y Conexiones80 Php. Campos Y Conexiones
80 Php. Campos Y ConexionesJosé M. Padilla
 
Tutorial estructuras algoritmicas instruciones secuenciales
Tutorial estructuras algoritmicas instruciones secuenciales    Tutorial estructuras algoritmicas instruciones secuenciales
Tutorial estructuras algoritmicas instruciones secuenciales Michele André
 
Ejercicios condicional-if
Ejercicios condicional-if  Ejercicios condicional-if
Ejercicios condicional-if yulieth licona
 
Intro aplicaciones web con php
Intro aplicaciones web con phpIntro aplicaciones web con php
Intro aplicaciones web con phpFer Nando
 
Fpr Tema6 www.fresymetal.com
Fpr Tema6 www.fresymetal.comFpr Tema6 www.fresymetal.com
Fpr Tema6 www.fresymetal.comFresyMetal
 

La actualidad más candente (17)

Lenguaje de Programacion - Java
Lenguaje de Programacion - JavaLenguaje de Programacion - Java
Lenguaje de Programacion - Java
 
Arreglos C#
Arreglos C#Arreglos C#
Arreglos C#
 
MANUAL CALCULADORA VOYAGE Capitulo 2 (6)
MANUAL CALCULADORA VOYAGE Capitulo 2 (6)MANUAL CALCULADORA VOYAGE Capitulo 2 (6)
MANUAL CALCULADORA VOYAGE Capitulo 2 (6)
 
Método de creación de contraseñas robustas de thi4 go
Método de creación de contraseñas robustas de thi4 goMétodo de creación de contraseñas robustas de thi4 go
Método de creación de contraseñas robustas de thi4 go
 
Ejercicios uml
Ejercicios umlEjercicios uml
Ejercicios uml
 
Problemas propuestos2.0
Problemas propuestos2.0Problemas propuestos2.0
Problemas propuestos2.0
 
63 Php. Imagenes Con Lineas Y Textos
63 Php. Imagenes Con Lineas Y Textos63 Php. Imagenes Con Lineas Y Textos
63 Php. Imagenes Con Lineas Y Textos
 
Tipos basicos de java
Tipos basicos de javaTipos basicos de java
Tipos basicos de java
 
Arreglos en c ++
Arreglos en c ++Arreglos en c ++
Arreglos en c ++
 
Interpolaion c++
Interpolaion c++Interpolaion c++
Interpolaion c++
 
80 Php. Campos Y Conexiones
80 Php. Campos Y Conexiones80 Php. Campos Y Conexiones
80 Php. Campos Y Conexiones
 
Tutorial estructuras algoritmicas instruciones secuenciales
Tutorial estructuras algoritmicas instruciones secuenciales    Tutorial estructuras algoritmicas instruciones secuenciales
Tutorial estructuras algoritmicas instruciones secuenciales
 
Ejercicios condicional-if
Ejercicios condicional-if  Ejercicios condicional-if
Ejercicios condicional-if
 
Separata java script
Separata java scriptSeparata java script
Separata java script
 
Funciones de cadenas
Funciones de cadenasFunciones de cadenas
Funciones de cadenas
 
Intro aplicaciones web con php
Intro aplicaciones web con phpIntro aplicaciones web con php
Intro aplicaciones web con php
 
Fpr Tema6 www.fresymetal.com
Fpr Tema6 www.fresymetal.comFpr Tema6 www.fresymetal.com
Fpr Tema6 www.fresymetal.com
 

Destacado

Destacado (20)

PM: Virtual Dimension Center (VDC) Fellbach erweitert sein Netzwerk erneut in...
PM: Virtual Dimension Center (VDC) Fellbach erweitert sein Netzwerk erneut in...PM: Virtual Dimension Center (VDC) Fellbach erweitert sein Netzwerk erneut in...
PM: Virtual Dimension Center (VDC) Fellbach erweitert sein Netzwerk erneut in...
 
Durkheim weber
Durkheim weberDurkheim weber
Durkheim weber
 
Jung 2x 98/99 Lg
Jung 2x 98/99 LgJung 2x 98/99 Lg
Jung 2x 98/99 Lg
 
Mäd 1x LG 98
Mäd 1x LG 98Mäd 1x LG 98
Mäd 1x LG 98
 
Introducción a la Administración
Introducción a la Administración Introducción a la Administración
Introducción a la Administración
 
Resumen CSC sept 2015
Resumen CSC sept 2015Resumen CSC sept 2015
Resumen CSC sept 2015
 
Ejercicio 7: Economía empresarial
Ejercicio 7: Economía empresarialEjercicio 7: Economía empresarial
Ejercicio 7: Economía empresarial
 
Kompetenzatlas Fahrsimulation
Kompetenzatlas FahrsimulationKompetenzatlas Fahrsimulation
Kompetenzatlas Fahrsimulation
 
Haaner Themengarten Band 3: Trautes Heim - Glück allein
Haaner Themengarten Band 3: Trautes Heim - Glück alleinHaaner Themengarten Band 3: Trautes Heim - Glück allein
Haaner Themengarten Band 3: Trautes Heim - Glück allein
 
Das Maus Labyrinth Version 1.0
Das Maus Labyrinth Version 1.0Das Maus Labyrinth Version 1.0
Das Maus Labyrinth Version 1.0
 
Gardenia carranza
Gardenia carranzaGardenia carranza
Gardenia carranza
 
Antroponimos
AntroponimosAntroponimos
Antroponimos
 
Los reyes católicos
Los reyes católicosLos reyes católicos
Los reyes católicos
 
De súbditos a ciudadanos
De súbditos a ciudadanosDe súbditos a ciudadanos
De súbditos a ciudadanos
 
Dispositivos de almacenamiento
Dispositivos de  almacenamientoDispositivos de  almacenamiento
Dispositivos de almacenamiento
 
Texto de-los-indicadores
Texto de-los-indicadoresTexto de-los-indicadores
Texto de-los-indicadores
 
Composite Simulation Roadmap
Composite Simulation RoadmapComposite Simulation Roadmap
Composite Simulation Roadmap
 
Regla mily jaja
Regla mily jajaRegla mily jaja
Regla mily jaja
 
Gabinetes
GabinetesGabinetes
Gabinetes
 
Proyecto cer san fransisco
Proyecto  cer san fransiscoProyecto  cer san fransisco
Proyecto cer san fransisco
 

Similar a Convertir nmeros-a-letras

Sistema de numeracion decimal de valor posicional
Sistema de numeracion decimal de valor posicionalSistema de numeracion decimal de valor posicional
Sistema de numeracion decimal de valor posicionalYaneth De Luna
 
Convrtit numeros a leras
Convrtit numeros a lerasConvrtit numeros a leras
Convrtit numeros a lerasjbersosa
 
27 Php. Arrays Bidimensionales
27 Php. Arrays Bidimensionales27 Php. Arrays Bidimensionales
27 Php. Arrays BidimensionalesJosé M. Padilla
 
Curso de php practico
Curso de php practicoCurso de php practico
Curso de php practicoNeox Dark
 
Capítulo 6 Qbasic manipulación de cadenas de texto
Capítulo 6 Qbasic  manipulación de cadenas de textoCapítulo 6 Qbasic  manipulación de cadenas de texto
Capítulo 6 Qbasic manipulación de cadenas de textoJulio Ayala Rolón
 
Suma de imagenes
Suma de imagenesSuma de imagenes
Suma de imagenesanar26
 
C:\fakepath\numeración binaria
C:\fakepath\numeración binariaC:\fakepath\numeración binaria
C:\fakepath\numeración binariacamila1991
 
Python, te lo explico con peras y manzanas
Python, te lo explico con peras y manzanasPython, te lo explico con peras y manzanas
Python, te lo explico con peras y manzanasWilmar Ruiz
 
Una de las variables en programación que pocas veces se utilizan por desconoc...
Una de las variables en programación que pocas veces se utilizan por desconoc...Una de las variables en programación que pocas veces se utilizan por desconoc...
Una de las variables en programación que pocas veces se utilizan por desconoc...Jose Martin Cueto Llanccaya
 
18 tipos-de-datos
18 tipos-de-datos18 tipos-de-datos
18 tipos-de-datosAndy T
 
Fonciones de cadenas
Fonciones de cadenasFonciones de cadenas
Fonciones de cadenasmkbenitez
 
Funciones de cadena
Funciones de cadenaFunciones de cadena
Funciones de cadenaAlfredo Joya
 
Fonciones de cadenas
Fonciones de cadenasFonciones de cadenas
Fonciones de cadenasmkbenitez
 

Similar a Convertir nmeros-a-letras (20)

P3si
P3siP3si
P3si
 
Sistema de numeracion decimal de valor posicional
Sistema de numeracion decimal de valor posicionalSistema de numeracion decimal de valor posicional
Sistema de numeracion decimal de valor posicional
 
Convrtit numeros a leras
Convrtit numeros a lerasConvrtit numeros a leras
Convrtit numeros a leras
 
27 Php. Arrays Bidimensionales
27 Php. Arrays Bidimensionales27 Php. Arrays Bidimensionales
27 Php. Arrays Bidimensionales
 
Tutorial pseudocodigo11
Tutorial pseudocodigo11Tutorial pseudocodigo11
Tutorial pseudocodigo11
 
Curso de php practico
Curso de php practicoCurso de php practico
Curso de php practico
 
Capítulo 6 Qbasic manipulación de cadenas de texto
Capítulo 6 Qbasic  manipulación de cadenas de textoCapítulo 6 Qbasic  manipulación de cadenas de texto
Capítulo 6 Qbasic manipulación de cadenas de texto
 
Suma de imagenes
Suma de imagenesSuma de imagenes
Suma de imagenes
 
CLASE 05.pdf
CLASE 05.pdfCLASE 05.pdf
CLASE 05.pdf
 
C:\fakepath\numeración binaria
C:\fakepath\numeración binariaC:\fakepath\numeración binaria
C:\fakepath\numeración binaria
 
Python, te lo explico con peras y manzanas
Python, te lo explico con peras y manzanasPython, te lo explico con peras y manzanas
Python, te lo explico con peras y manzanas
 
Abc algoritmos
Abc algoritmos Abc algoritmos
Abc algoritmos
 
Una de las variables en programación que pocas veces se utilizan por desconoc...
Una de las variables en programación que pocas veces se utilizan por desconoc...Una de las variables en programación que pocas veces se utilizan por desconoc...
Una de las variables en programación que pocas veces se utilizan por desconoc...
 
01 el lenguaje Python
01 el lenguaje Python01 el lenguaje Python
01 el lenguaje Python
 
18 tipos-de-datos
18 tipos-de-datos18 tipos-de-datos
18 tipos-de-datos
 
Fonciones de cadenas
Fonciones de cadenasFonciones de cadenas
Fonciones de cadenas
 
Funciones de cadena
Funciones de cadenaFunciones de cadena
Funciones de cadena
 
Fonciones de cadenas
Fonciones de cadenasFonciones de cadenas
Fonciones de cadenas
 
Funciones
FuncionesFunciones
Funciones
 
Nombr
NombrNombr
Nombr
 

Convertir nmeros-a-letras

  • 1. RUTINA PARA CONVERTIR UNA CANTIDAD NUMÉRICA EN LETRAS. MOTIVACIÓN. En algún momento, mientras navegábamos en Internet o mientras analizábamos el código que “descubrimos” en el trabajo de nuestro amigo Nerdio, nos encontramos con algún script/función/clase que convertía cadenas numéricas a su equivalente en palabras (en letras, dirían algunos). Seguramente en ese momento ustedes pensaron lo mismo que yo: “¿para qué me puede servir eso? Nunca voy a escribir un cheque usando PHP ...”. Y llegados a esa conclusión simplemente seguimos a otra página web, borramos esa parte del código o la imprimimos en un papel que nunca más encontramos. Y entonces llega el día en que el jefe abre la boca y salen estas palabras: “Quiero que los cheques (facturas, recibos, etc.) las haga el sistema” (Así le dicen siempre, “el sistema”). Claro que es más fácil ponerlo a uno a codificar que decirle a la secre que se salga de facebook y escriba un cheque en la bendita máquina de escribir. Y es entonces cuando buscamos, rebuscamos y no encontramos la dichosa rutina. Así que llegamos al problema: ¿y ahora cómo diablos hago para que mi número salga convertido en letras? Se encuentran algunas funciones para eso en Internet (personalmente probé varias) pero como sucede con todo código que no es nuestro, si queremos modificar algo primero tenemos que entender cómo funciona y lo más probable es que pasemos varias horas sin entender nada. Y a menos que esté bien que nuestros cheques digan algo así como “двести пятьдесят рублей”, tenemos que modificar algo. Así que me dediqué a elaborar mi propia rutina y, siendo de buen corazón, ahora quiero compartirla con ustedes, pero no solamente quiero entregarles el código sino también el razonamiento detrás del código. No pretendo que sea la rutina mejor pensada ni la mejor codificada. Estoy seguro de que hay margen para perfeccionarla y al menos cinco formas de hacerla mejor, pero, parafraseando el dicho: “no hay código por malo que sea que no nos deje algo bueno”. Como siempre, los comentarios son bien recibidos. PARA LOS IMPACIENTES ¿No te interesa nada de lo que tengo que decir? Ve directamente a la sección EL SCRIPT, COMPLETO, allí está todo el código. Adaptaciones que hay que hacer: en la función num2letras() hay que cambiar el nombre de la moneda (la variable $moneda) y arreglar el plural al final de la función. También hay que tener en cuenta que pueden introducirse comillas tipográficas (“”) al hacer copy/paste. ¿La forma de uso?: ver la sección “¿Y CÓMO USO TODO ESO?”. Mis disculpas por hacerte trabajar. ANALISIS DEL SCRIPT ALGUNOS DATOS PREVIOS 1. Este script maneja números de hasta 15 cifras (más dos decimales), o sea, llega hasta 999 billones y centavos. Esto es así debido a problemas de precisión que surgen al trabajar con números grandes. 2. En mi país, Guatemala, utilizamos como moneda el Quetzal. Decidí poner el nombre de la moneda en una variable al principio del código porque me imagino que, si estás leyendo esto, es bastante probable que tengas que cambiarlo (a menos que seas de Guatemala,
  • 2. claro). La forma del plural se arregla al final del script, cuando ya sabemos exactamente con qué cantidad estamos tratando. 3. Utilizamos dos funciones: enletras() y num2letras(). La primera convierte un número de tres cifras en su equivalente en palabras. La segunda función se apoya en la primera: separa un número de hasta 15 dígitos en grupos de tres cifras y pasa cada uno de esos grupos a la primera función para su procesamiento. Por último, esta segunda función da formato a la cadena de palabras obtenida para su presentación correcta. LA FUNCIÓN ENLETRAS() La función enletras() toma un solo parámetro: una cadena de caracteres 1 que representa un número de hasta tres cifras y que se pasa desde la función num2letras() y devuelve ese número convertido en palabras: function enletras($valor) { -- código php return $en_letras; } Básicamente, en español2, podemos “decir” cualquier número usando grupos de tres dígitos y concatenando esos grupos con calificadores del nivel. Así por ejemplo, el número 11,234,567 será “once millones doscientos treinta y cuatro mil quinientos sesenta y siete”, donde los calificadores son las palabras “millones” y “mil”, que concatenan los tres grupos “once”, “doscientos treinta y cuatro” y “quinientos sesenta y siete”. Teniendo lo anterior en cuenta, podemos crear tres matrices (una para las unidades, otra para las decenas y una tercera para las centenas) que nos permiten formar el nombre de cualquier grupo de tres dígitos. Hay que tener en cuenta que los números 11 al 19 tienen su propio nombre, mientras que los otros números utilizan una combinación de nombres más sencillos (por ejemplo, 43 = “cuarenta y tres”). Las matrices serán: $uni[0] = "cero"; $uni[1] = "un"; $uni[2] = "dos"; $uni[3] = "tres"; $uni[4] = "cuatro"; $uni[5] = "cinco"; $uni[6] = "seis"; $uni[7] = "siete"; $uni[8] = "ocho"; $uni[9] = "nueve"; $uni[11] = "once"; $uni[12] = "doce"; $uni[13] = "trece"; $uni[14] = "catorce"; $uni[15] = "quince"; $uni[16] = "dieciseis"; $uni[17] = "diecisiete"; $uni[18] = "dieciocho"; $uni[19] = "diecinueve"; $dec[2] = "veinti"; $dec[3] = "treinta y "; 1 No es lo mismo 'una cadena de caracteres que representa un número' que 'un número'. Ver la documentación de PHP: http://www.php.net/manual/es/language.types.php 2 Castellano, para los puristas.
  • 3. $dec[4] = "cuarenta y "; $dec[5] = "cincuenta y "; $dec[6] = "sesenta y "; $dec[7] = "setenta y "; $dec[8] = "ochenta y "; $dec[9] = "noventa y "; $dec[10] = "diez"; $dec[20] = "veinte"; $dec[30] = "treinta"; $dec[40] = 'cuarenta'; $dec[50] = 'cincuenta'; $dec[60] = 'sesenta'; $dec[70] = 'setenta'; $dec[80] = 'ochenta'; $dec[90] = 'noventa'; $cen[1] = "ciento "; $cen[2] = "doscientos "; $cen[3] = "trescientos "; $cen[4] = "cuatrocientos "; $cen[5] = "quinientos "; $cen[6] = "seiscientos "; $cen[7] = "setecientos "; $cen[8] = "ochocientos "; $cen[9] = "novecientos "; $cen[100] = "cien"; $cen[200] = "doscientos"; $cen[300] = "trescientos"; $cen[400] = "cuatrocientos"; $cen[500] = "quinientos"; $cen[600] = "seiscientos"; $cen[700] = "setecientos"; $cen[800] = "ochocientos"; $cen[900] = "novecientos"; Como primer paso, determinamos el largo de la cadena que la función recibió como parámetro. Este largo nos dice cuántos dígitos tiene el grupo y, según sea el valor obtenido, así lo procesaremos. $largo = strlen($valor); La variable $largo puede tomar solamente tres valores: 1, 2 ó 3. Debemos recordar que una cadena de caracteres también es una matriz y cada uno de sus elementos puede identificarse con un índice. Por ejemplo, para la cadena $var = 'ABC', sus elementos serán $var[0] = 'A', $var[1] = 'B' y $var[2] = 'C'. De la misma manera, podemos usar $valor[0], $valor[1] y $valor[2] para identificar cada uno de los dígitos del parámetro $valor que se ha pasado a la función. Usaremos un lazo switch() para decidir cómo procesaremos el parámetro $valor. switch($largo) { case 1: $en_letras = $uni[$valor[0]]; break; El primer caso es el más sencillo. Hemos recibido una cadena que contiene un solo caracter: un dígito cuyo valor se encuentra entre 1 y 9. Para este caso, simplemente buscamos el valor correspondiente en la matriz $uni[] y lo asignamos a la variable $en_letras. case 2:
  • 4. $decena = $valor[0] . $valor[1]; if($valor[1] == 0) { // decena exacta $en_letras = $dec[$decena]; } else { if($valor[0] > 1) { // entre 21 y 99 $en_letras = $dec[$valor[0]] . $uni[$valor[1]]; } else { // entre 11 y 19 $en_letras = $uni[$decena]; } } break; El segundo caso trata con cadenas de dos caracteres: un número de dos dígitos cuyo valor se encuentra entre 11 y 99. Aquí pueden darse dos situaciones diferentes: que tengamos una decena exacta (10, 20, 30, etc.) o que no lo sea. Si el valor del último dígito ($valor[1]) es cero, la decena será exacta y buscamos su “nombre” en la matriz $dec[]. Para ello usamos la variable $decena, formada por los dígitos $valor[0] y $valor[1]. Si no se trata de una decena exacta (por ejemplo: 34, 57, 89, etc.), aún tenemos dos opciones que considerar: que se trate de números entre 11 y 19, en cuyo caso buscamos su nombre en la matriz $uni[], o que se trate de un número mayor. En este último caso concatenamos el nombre de la decena obtenido de la matriz $dec[] con el nombre de la unidad obtenido de la matriz $uni[]. case 3: // centenas, decenas y unidades $centena = $valor[0] . $valor[1] . $valor[2]; if($centena != '000') { $decena = $valor[1] . $valor[2]; if($valor[1] == 0 AND $valor[2] == 0) { // centena exacta $en_letras = $cen[$centena]; } else { if($valor[2] == 0) { // decena exacta $en_letras = $dec[$decena]; } else { if($valor[1] == 0 AND $valor[2] == 1) { // exactamente 1 $en_letras = $uni[$valor[2]]; } elseif($valor[1] > 1) { // entre 21 y 99 $en_letras = $dec[$valor[1]] . $uni[$valor[2]]; } else { // entre 11 y 19 $en_letras = $uni[$decena]; } } $en_letras = $cen[$valor[0]] . $en_letras; } } else { $en_letras = "000"; } break; }
  • 5. Veamos ahora el último caso: una cadena que representa un número de tres dígitos (entre 100 y 999). Comprobamos que la cadena no sea igual a '000' (por ejemplo, si tenemos originalmente un número como 1,000 y estamos procesando el grupo de la derecha). Si la cadena es '000', simplemente devolvemos esa misma cadena para manejarla en la función num2letras(), como se verá más adelante. Ahora veamos las decenas. Si los dígitos $valor[1] y $valor[2] son iguales a cero, tenemos una centena exacta (100, 200, 300, etc.). En este caso buscamos el nombre directamente en la matriz $cen[]. Como en el caso anterior del switch(), revisamos si estamos tratando con una decena exacta. Si es así, obtenemos su nombre directamente de la matriz $dec[]. También debemos comprobar otra situación especial: el segundo dígito es cero y el tercero es '1' (101, 201, 301, etc.) En este caso asignamos temporalmente a la variable $en_letras el valor 'UN'. Y decimos que es temporalmente porque un poco más abajo se decide si conservamos este valor o no. Por último y como en el segundo caso del switch() (la cadena de dos números), si los últimos dos dígitos se encuentran en el rango entre 11 y 99, buscamos su nombre en la matriz $uni[]. Si se encuentran entre 21 y 99 (el número 20 ya se trato antes como una decena exacta), su nombre será la combinación de el nombre de la decena en la matriz $dec[] y la unidad en la matriz $uni[]. Ahora obtenemos el nombre de la centena de la matriz $cen[] y le agregamos el valor de la variable $en_letras (que contiene el nombre de los dos dígitos restantes). Una vez que llegamos aquí, la variable $en_letras contiene el “nombre” del grupo de tres dígitos que se pasó como parámetro y solo nos queda devolverlo. return $en_letras; LA FUNCIÓN NUM2LETRAS() Esta función se ocupa básicamente de darle un formato adecuado para su impresión al valor devuelto por la función $enletras() .Toma como parámetro un número que será el que queremos convertir de dígitos a “palabras”. function num2letras($val) { -- código php } Definimos una variable que contendrá el nombre de la moneda. Uso una variable porque es más sencillo de adaptar. En mi país, Guatemala, usamos el Quetzal como moneda. $moneda = 'QUETZAL'; Como el número que se pasará a esta función puede venir “contaminado” con espacios, comas, símbolos de moneda, etc., debemos limpiarlo para quedarnos solamente con dígitos y, tal vez, un punto decimal. La función eregi_replace() toma como patrón las letras A-Z (mayúsculas o minúsculas) y el símbolo '$'. Eso cubre casi todos los símbolos de monedas de los países hispanoparlantes, pero si estas en un país que usa otro símbolo (por ejemplo, el yen), éste se agrega aquí. $num = eregi_replace("[A-Za-z$, ]", "", $val); Hay que convertir nuestro número a número. No, todavía no estoy desvariando. Suena confuso, pero hasta ahora hemos trabajado con cadenas de caracteres que representan números, no con números verdaderos. Sin embargo, necesitamos que nuestro número lo sea realmente, para darle formato con dos decimales y los miles separados por comas. ¿Para qué ponerle comas después de que se las quitamos? La importancia de esto se verá más abajo.
  • 6. $num = (float) $num; Como dijimos, necesitamos un número. Además, al convertir nuestra cadena usando float(), nos aseguramos de desaparecer los ceros a la izquierda. $num = number_format($num, 2, ".", ","); Si anteriormente no no podíamos estar seguros de cómo estaba formado nuestro número, en este momento ya tenemos con seguridad un número con un punto y dos decimales. Lo separamos en dos partes usando el punto decimal como referencia. Por ahora solo manejaremos las cantidades enteras y nos ocuparemos de los decimales (los centavos) al final del script. $enteros = substr($num, 0, -3); Una vez que sabemos cuál es el valor de la parte entera, lo descomponemos en grupos de tres dígitos usando las comas que introdujimos anteriormente (les dije que eran importantes). Si nuestro número originalmente traía comas, no podíamos confiar en que estaban en el lugar correcto, así que lo que hicimos fue asegurarnos de que el número tuviera el formato exactamente como lo necesitamos. $trios = explode(",", $enteros); Ahora algunas inicializaciones necesarias: creamos la matriz $partes() que contendrá como elementos los “nombres” que vamos obteniendo de la función enletras(); un contador $n = 0, que será el índice de cada elemento que se agregue a esa matriz; y una variable vacía, $en_letras, en la que se formará el “nombre” de nuestro número. $partes = array(); $n = 0; $en_letras = ''; Ahora recorremos cada grupo de números. Como dijimos al principio, por asuntos relacionados con la precisión, solamente manejaremos hasta 15 dígitos. Si cada grupo contiene tres dígitos (o menos), debemos hacer el siguiente procedimiento un máximo de cinco veces. for($i = 5; $i >= 0; $i--) { Primero comprobamos la longitud del grupo, para saber si en realidad existe. Asumimos que tenemos 15 dígitos, pero esto puede no ser verdad. $largo = strlen($trios[$i]); Si el grupo existe ($largo tiene un valor), vamos a procesar el grupo. Más arriba habíamos dividido el número en grupos (almacenados en la matriz $trios[]). Como nuestro lazo inicia con $i = 5 y va disminuyendo, quiere decir que el primer grupo que enviaremos a la función enletras() será $trios[5], que es el trío de la extrema derecha (el de menor nivel) y a partir de allí nos moveremos hacia la izquierda hasta llegar al trío de la extrema izquierda, el de mayor nivel. La función enletras() procesa cada trío y, como ya vimos antes, devuelve su valor expresado en palabras. Ese valor se agrega como un nuevo elemento (iniciando con el índice $n = 0 y aumentando de valor) a la matriz $partes[]. Ese proceso se repite para cada iteración del lazo for(). if($largo) { $partes[$n] = enletras($trios[$i]); En cada iteración también analizamos el contenido del elemento $partes[$n] que acabamos de agregar.
  • 7. Veamos el primer elemento (número <= 999). Como ya dijimos antes, si se da el caso de que el grupo este formado solamente por ceros (por ejemplo, el grupo de la derecha en el número 1,000), la función devuelve esa misma cadena ('000') y la variable $en_letras toma el valor “DE“. Si el número no es 000, $en_letras toma el valor contenido en el elemento $partes[0]. if($partes[0] != '000') { $en_letras = $partes[0]; } else { $en_letras = ' DE '; } Analicemos el segundo grupo desde la derecha, el de los miles. Si está vacío (nuestro número solo tenía tres dígitos), simplemente continuamos hasta el final del lazo para otra iteración. Si el valor de la variable $en_letras es solamente “DE“, vaciamos esa variable (por ejemplo, si nuestro número es 1,000 no se dice “un mil de quetzales”, sino “un mil quetzales”. La palabra “de” sobra). Si $en_letras tiene otro valor (por ejemplo: “trescientos setenta y uno”), concatenamos el valor devuelto por la función enletras() para este trío con el ya existente en la variable $en_letras, para formar un nombre que contiene miles, por ejemplo: “dos mil trescientos setenta y uno”. if($partes[1] != '') { if($en_letras == ' DE ') { $en_letras = ''; } $en_letras = $partes[1] . " MIL " . $en_letras; } Para el tercer grupo desde la derecha, el de los millones, el procedimiento es similar. Si existe, lo procesamos, si no, continuamos hasta el final del lazo. Como nuestro calificador puede ser “millón” o “millones”, primero decidimos cuál usaremos según sea el valor contenido en $partes[2]. Si $partes[1] y $partes[0] (los dos grupos de la derecha) son diferentes de '000', asignamos a la variable $en_letras todos esos valores (por ejemplo: cinco millones dos mil trescientos setenta y uno). En el caso de que $partes[1] y $partes[0] sean ambas iguales a '000' (por ejemplo: 1,000,000), la concatenación será “un millón de”. if($partes[2] != '') { $millones = " MILLONES "; if(strtoupper($partes[2]) == 'UN') { $millones = " MILLON "; } if($partes[0] != '000' AND $partes[1] != '000') { $en_letras = $partes[2] . $millones . $en_letras; } else { $en_letras = $en_letras = $partes[2] . $millones . ' DE '; } } El cuarto grupo (miles de millones) sigue un análisis similar. Si este grupo existe y los otros grupos no contienen únicamente ceros, concatenamos esos valores. Siguiendo nuestro ejemplo, $en_letras contiene en este momento el valor “cinco millones dos mil trescientos setenta y uno” y luego de procesar este grupo contendrá “ochenta mil cinco millones dos mil trescientos setenta y uno”. Si los otros grupos todos contienen '000' (1,000,000,000), $en_letras será igual a “un mil millones de”. if($partes[3] != '') { if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000') { $en_letras = $partes[3] . " MIL " . $en_letras; } else {
  • 8. $en_letras = $partes[3] . " MIL MILLONES DE "; } } Finalmente, el quinto grupo, el de la extrema izquierda. El análisis es también parecido. Primero decidimos qué calificador utilizaremos: “billón” o “billones”. Luego, si los restantes grupos tienen un valor, concatenamos esos valores y los asignamos a la variable $en_letras. En caso contrario (todos los otros grupos contienen '000'), nuestro nombre será “1 billón de”. if($partes[4] != '') { $billones = " BILLONES "; if(strtoupper($partes[4]) == 'UN') { $billones = " BILLON "; } if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000' AND $partes[3] != '000') { $en_letras = $partes[4] . $billones . $en_letras; } else { $en_letras = $partes[4] . $billones . ' DE '; } } $n++; } } Ahora debemos ocuparnos del plural de la moneda. Como ya indiqué, en mi país la moneda nacional es el Quetzal. Plural: quetzales (se agrega 'es'). La regla es sencilla: si el nombre de la moneda termina en vocal, se agrega una 's' (peso → pesos, lempira → lempiras, etc.) en otro caso, se agrega 'es' (quetzal → quetzales, bolivar → bolívares, etc.) if(strtoupper($en_letras) != 'UN') { $moneda = " " . $moneda . "ES"; } Vamos a ocuparnos de los centavos. La presentación que usamos aquí es 'XX/100', donde XX representa la cantidad en centavos (por ejemplo: 35/100). También podría usarse 'XX centavos' o, en el caso de que sean cero centavos decir 'tantos quetzales exactos'. Eso queda al gusto del contador de la empresa o de la persona a cargo de los cheques/recibos/facturas o lo que sea. En cualquier caso, la siguiente parte del script puede modificarse fácilmente. $centavos = substr($num, -2) . "/100"; Por último, lo ponemos todo junto y devolvemos nuestro resultado. $letras = strtoupper($en_letras) . $moneda . " CON " . $centavos; return $letras; ¿Y CÓMO USO TODO ESO? En la práctica, en este momento ya debemos tener una cantidad disponible. Ya sea porque la calculamos, porque viene de un formulario mediante $_POST[], estaba almacenada en una tabla de nuestra base de datos o simplemente existe en alguna forma. Cuando llega el momento de imprimir esa cantidad, simplemente hacemos la llamada: $echo num2letras($total_en_numero);
  • 9. CONCLUSIONES Como dije antes, seguramente hay una forma mejor y más eficiente de codificar todo esto. Pueden modificar este script a su gusto, no hay problema con eso. Si han aprendido algo al leer este trabajo, me doy por satisfecho. Sus comentarios, críticas y sugerencias son siempre bienvenidos, sobre todo si se refiere a errores de código o tipográficos. Y, como siempre, recuerden que usan este script por su cuenta y riesgo. Funciona como debe de funcionar y hace lo que dice que hace (y nada más), pero si algo les pasa (se les calienta la cerveza, los deja la novia/el novio, les da gripe, los despiden del trabajo, reprueban el exámen, etc.), no me culpen a mí. Juan Carlos Vásquez jcvasquez07@gmail.com EL SCRIPT, COMPLETO Recuerden: – Hay que adaptar la variable $moneda y el plural al final de la función num2letras(). – Al hacer copy/paste pueden introducirse comillas tipográficas (“”). Hay que sustituirlas por comillas normales en su editor preferido. function enletras($parte) { // toma como parámetro un numero entero de hasta tres cifras $uni[0] = "cero"; $uni[1] = "un"; $uni[2] = "dos"; $uni[3] = "tres"; $uni[4] = "cuatro"; $uni[5] = "cinco"; $uni[6] = "seis"; $uni[7] = "siete"; $uni[8] = "ocho"; $uni[9] = "nueve"; $uni[11] = "once"; $uni[12] = "doce"; $uni[13] = "trece"; $uni[14] = "catorce"; $uni[15] = "quince"; $uni[16] = "dieciseis"; $uni[17] = "diecisiete"; $uni[18] = "dieciocho"; $uni[19] = "diecinueve"; $dec[2] = "veinti"; $dec[3] = "treinta y "; $dec[4] = "cuarenta y "; $dec[5] = "cincuenta y "; $dec[6] = "sesenta y "; $dec[7] = "setenta y "; $dec[8] = "ochenta y "; $dec[9] = "noventa y "; $dec[10] = "diez"; $dec[20] = "veinte"; $dec[30] = "treinta"; $dec[40] = 'cuarenta'; $dec[50] = 'cincuenta'; $dec[60] = 'sesenta';
  • 10. $dec[70] = 'setenta'; $dec[80] = 'ochenta'; $dec[90] = 'noventa'; $cen[1] = "ciento "; $cen[2] = "doscientos "; $cen[3] = "trescientos "; $cen[4] = "cuatrocientos "; $cen[5] = "quinientos "; $cen[6] = "seiscientos "; $cen[7] = "setecientos "; $cen[8] = "ochocientos "; $cen[9] = "novecientos "; $cen[100] = "cien"; $cen[200] = "doscientos"; $cen[300] = "trescientos"; $cen[400] = "cuatrocientos"; $cen[500] = "quinientos"; $cen[600] = "seiscientos"; $cen[700] = "setecientos"; $cen[800] = "ochocientos"; $cen[900] = "novecientos"; $largo = strlen($parte); // Segun el largo del numero, tenemos unidades // decenas y centenas switch($largo) { case 1: // solo unidades $en_letras = $uni[$parte[0]]; break; case 2: // decenas y unidades $decena = $parte[0] . $parte[1]; if($parte[1] == 0) { // decena exacta $en_letras = $dec[$decena]; } else { if($parte[0] > 1) { // entre 21 y 99 $en_letras = $dec[$parte[0]] . $uni[$parte[1]]; } else { // entre 11 y 19 $en_letras = $uni[$decena]; } } break; case 3: // centenas, decenas y unidades $centena = $parte[0] . $parte[1] . $parte[2]; if($centena != '000') { $decena = $parte[1] . $parte[2]; if($parte[1] == 0 AND $parte[2] == 0) { // centena exacta $en_letras = $cen[$centena]; } else { if($parte[2] == 0) { // decena exacta $en_letras = $dec[$decena];
  • 11. } else { if($parte[1] == 0 AND $parte[2] == 1) { // exactamente 1 $en_letras = $uni[$parte[2]]; } elseif($parte[1] > 1) { // entre 21 y 99 $en_letras = $dec[$parte[1]] . $uni[$parte[2]]; } else { // entre 11 y 19 $en_letras = $uni[$decena]; } } $en_letras = $cen[$parte[0]] . $en_letras; } } else { $en_letras = "000"; } break; } return $en_letras; } function num2letras($val) { $moneda = 'QUETZAL'; $num = eregi_replace("[A-Za-z, ]", "", $val); $num = (float) $num; $num = number_format($num, 2, ".", ","); // Solo manejamos los enteros. Los centavos los agregamos al final $enteros = substr($num, 0, -3); $trios = explode(",", $enteros); $partes = array(); $n = 0; $en_letras = ''; for($i = 5; $i >= 0; $i--) { $largo = strlen($trios[$i]); if($largo) { // aqui procesamos las partes $partes[$n] = enletras($trios[$i]); if($partes[0] != '000') { $en_letras = $partes[0]; } else { $en_letras = ' DE '; } if($partes[1] != '') { if($en_letras == ' DE ') { $en_letras = ''; } $en_letras = $partes[1] . " MIL " . $en_letras; } if($partes[2] != '') { $millones = " MILLONES "; if(strtoupper($partes[2]) == 'UN') { $millones = " MILLON "; }
  • 12. if($partes[0] != '000' AND $partes[1] != '000') { $en_letras = $partes[2] . $millones . $en_letras; } else { $en_letras = $en_letras = $partes[2] . $millones . ' DE '; } } if($partes[3] != '') { $en_letras = $partes[3] . " MIL " . $en_letras; if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000') { $en_letras = $partes[3] . " MIL " . $en_letras; } else { $en_letras = $partes[3] . " MIL MILLONES DE "; } } if($partes[4] != '') { $billones = " BILLONES "; if(strtoupper($partes[4]) == 'UN') { $billones = " BILLON "; } if($partes[0] != '000' AND $partes[1] != '000' AND $partes[2] != '000' AND $partes[3] != '000') { $en_letras = $partes[4] . $billones . $en_letras; } else { $en_letras = $partes[4] . $billones . ' DE '; } } $n++; } } if(strtoupper($en_letras) != 'UN') { $moneda = " " . $moneda . "ES"; } // Los centavos $centavos = substr($num, -2) . "/100"; $letras = strtoupper($en_letras) . $moneda . " CON " . $centavos; return $letras; }