miércoles, 27 de octubre de 2010

Emular strlwr

Haciendo un proyecto de clase, en la propia clase funcionaba a la perfección. Al ser uno de los objetivos del programa, ser compatible tanto en Windows (En clase usamos Win 7 y Dev-C++ como IDE) como en GNU/Linux, he tenido que crear una pequeña función emulando el funcionamiento de una función que no es ANSI C: strlwr(). El código es simple y el algoritmo mas, y me parece que es interesante para todos los que quieran emular strlwr o similar bajo GNU/Linux.



Algoritmo: strlwr devuelve una cadena, y se le llama con otra cadena como parámetro. La cadena que le pasamos como parámetro, se convertirá automáticamente a a minúsculas. No afectará a números ni otro tipo de caracteres. Devolveremos la cadena transformada a minúsculas.

  1. Introducir cadena
  2. Contar caracteres de cadena
  3. Recorrer cadena carácter a carácter
    1. Si el carácter es una letra en Mayúscula
      1. Cambiamos por letra en minúscula
    2. Si no
      1. Nada.
  4. Devolvemos cadena transformada.
Como vemos, es fácil de implementar. Para agilizar nuestro trabajo, podemos agregar la cabecera ctype.h  para usar la función tolower(). tolower() , que si es ANSI C, convierte un carácter a minúscula, por lo cual, lo podemos usar en el paso 3.1.1 de nuestro algoritmo.

Veamos ahora un código que implementa el algoritmo. He llamado a la función strmtm().

/* Requiere ctype.h y string.h */
char * strmtm(char * tomin)
{   
    int cont = strlen(tomin); int i;

    for (i=0;i<cont;i++)
    {
        switch (tomin[i])
        {
            case 'A': 
            case 'B': 
            case 'C': 
            case 'D': 
            case 'E': 
            case 'F':
            case 'G':
            case 'H':
            case 'I':
            case 'J': 
            case 'K':
            case 'L': 
            case 'M': 
            case 'N': 
            case 'Ñ': 
            case 'O': 
            case 'P':
            case 'Q': 
            case 'R': 
            case 'S': 
            case 'T': 
            case 'U': 
            case 'V': 
            case 'W': 
            case 'X': 
            case 'Y': 
            case 'Z':  tomin[i] = tolower(tomin[i]); break;
        }
    }
    return tomin;
}

Como vemos, comprobamos 1 a 1 cada carácter. En caso de ser una letra en mayúsculas, convertimos el carácter a minúscula. Fácil.

Evidentemente podemos hacer lo mismo pero a la inversa, para convertir los caracteres en minúscula a mayúscula dentro de una cadena. Solo habría que cambiar el paso 3.1.1 del algoritmo.

A modo de ejemplo, y como estoy tratando de aprender Python, pongo un pequeño ejemplo, hecho con la ayuda de Rock:

def strlwr(cadena):
    cadena2 = ''"
    for i in cadena:
        num = ord(i)
        if num >= 65 and num <= 90:
            num += 32
            cadena2 = cadena2 + chr(num)

        else:
            cadena2 = cadena2 + chr(num)

    return cadena2

Usamos un for (En Python for se usa para iterar sobre listas. P. ej. for element in list: acción) Para recorrer la cadena, y usamos las funciones chr y ord para cambiar los caracteres.

Nota: No funciona con el carácter Ñ. ¿Alguien me ayuda con eso? ^^



Nota 2: Pasteo la mejora que propone Anibal en un comentario. Pensaba que tolower sumaba 32 a cualquier carácter ASCII, de ahí que me complicara tanto en la función. Visto que no,

for (int i=0; i < strlen(cadena); ++i)
{
   cadena[i] = tolower(cadena[i]);
}

6 comentarios:

  1. Otra cosa que puedes hacer es asignar la letra a una variable tipo entero mediante casting, asi le da el equivalente ASCII de la letra al número y no tienes que hacer ese enorme switch ^^

    Supongo que con la Ñ algo parecido funcionaría. Es cuestión de probar.

    ResponderEliminar
  2. tolower
    El algoritmo se vuelve muchísimo más sencillo:
    for(int i=0; i < strlen(cadena); ++i){
    cadena[i] = tolower(cadena[i]);
    }

    ResponderEliminar
  3. Vaya, no sé donde me pareció leer que tolower() sumaba 32 al carácter ASCII por lo que pensé que daría problemas con algunos caracteres.

    Viendo el ejemplo, con tu permiso lo voy a añadir a la entrada.

    Gracias como siempre.

    ResponderEliminar
  4. Quita el siwtch y pon :
    if ((tomin[i] >= 'A') && (tomin[i] <= 'Z'))
    switch (tomin[i])

    Hace lo mismo.

    ResponderEliminar
  5. Mi pregunta, es ... os han explicado como son las cadenas en C y entendéis pq el strlen sobra para esta función ?

    ejemplo :
    int i= 0;
    while (cadena[i] != 0)
    {
    if ((cadena[i] >= 'A') && (cadena[i] <= 'Z') || cadena[i] == 'Ñ')
    cadena[i] = tolower(cadena[i]);
    i++;
    }

    Nota : Pq usar tolower en vez de restar o sumar 32 -> tolower tiene en cuenta las locales del sitema .. es decir la letra Ñ. Las tres fuera del ASCII original de 7 bits no tiene pq cumplir de que estén a 32 posiciones de mayuscula/minuscula.

    Del man :
    The details of what constitutes an uppercase or lowercase letter depend on the current locale.
    For example, the default "C" locale does not know about umlauts, so no conversion is done for them.
    In some non-English locales, there are lowercase letters with no corresponding uppercase equivalent; the German sharp s is one example.

    ResponderEliminar
  6. PD : Disculpad mi torpeza de teclado. El maldito corrector de Chrome es una M...
    PPD: En mi primera entrada tengo un gazapo producto de escribir a prisa para ir me a comer después de clase....

    ResponderEliminar