Funciones PROPIAS para manejo de strings en C/C++

Iniciado por Álvaro G. Tenorio, 2 Septiembre 2013, 17:34 PM

0 Miembros y 4 Visitantes están viendo este tema.

Álvaro G. Tenorio

He estado investigando últimamente sobre este tema del manejo de cadenas de caracteres en C y C++ y os traigo un código con 3 funciones que pueden resultar interesantes para este tema.

La primera función es un sustituto de strlen();

La segunda es un sucedáneo de toupper(); pero que cambia todo el texto de minúsculas a mayúsculas y no solo un caracter concreto.

La última es lo mismo que la anterior peor con tolower();

Código (cpp) [Seleccionar]

/*#include <stdio.h> en C*/
#include <cstdio>

/*Prototipos de funciones utiles para el manejo de cadenas de caracteres*/
inline size_t magnitudTexto (char texto []);
inline void textoMayusculas (char texto [], size_t numeroCaracteres);
inline void textoMinusculas (char texto [], size_t numeroCaracteres);

/*Funcion main*/
int main (void)
{
   /*Declaracion de variables*/
   const unsigned char CARACTERES_MAXIMOS = 140; /*Constante para no usar numeros magicos*/
   char texto [CARACTERES_MAXIMOS]; /*Array de caracteres de 140 espacios*/
   unsigned char caracteresTexto = 0;

   /*Tramites con el usuario*/
   printf("Introduzca un texto: \n");
   fgets(texto, CARACTERES_MAXIMOS, stdin); /*Uso de fgets(); para evitar desbordamientos de buffer*/

   /*Comprobaciones del funcionamiento de las funciones*/
   caracteresTexto = magnitudTexto(texto);
   printf("El texto tiene %hhu caracteres.\n", caracteresTexto); /*%hhu para variables unsigned char, de 0 a 255*/

   textoMayusculas(texto, caracteresTexto);
   printf("El texto pasado a mayusculas:\n%s", texto);

   textoMinusculas(texto, caracteresTexto);
   printf("El texto pasado a minusculas:\n%s", texto);

   return 0;
}

inline size_t magnitudTexto (char texto [])
{
   size_t magnitud = 0;

   while (texto [magnitud] != '\0') /*Los arrays de caracteres finalizan con un \0*/
   {
       magnitud++;
   }

   return magnitud;
}

inline void textoMayusculas (char texto [], size_t numeroCaracteres)
{
   size_t contador = 0;

   while (contador <= numeroCaracteres)
   {
       if ((texto [contador] >= 'a') && (texto [contador] <= 'z'))
       {
           texto [contador] -= 'a' - 'A'; /*Consultar la tabla ASCII*/
           contador++;
       }
       else
       {
           contador++;
       }
   }
}

inline void textoMinusculas (char texto [], size_t numeroCaracteres)
{
   size_t contador = 0;

   while (contador <= numeroCaracteres)
   {
       if ((texto [contador] >= 'A') && (texto [contador <= 'Z']))
       {
           texto [contador] += 'a' - 'A';
           contador++;
       }
       else
       {
           contador++;
       }
   }
}


Decidme qué os parece, si funciona correctamente y demás, yo lo he probado compilandolo en Code::Blocks en C++ bajo el compilador g++ en Ubuntu 13.04, debería funcionar en Windows también.

Corregido el error del static.
Corregido el error de tipos demasiado cortos (unsigned char).
Corregido el error de "+- 32".

eferion

#1
No lo tomes a mal, pero no veo que beneficios aporta la función "magnitudTexto" sobre la función strlen. Además tampoco entiendo por qué razón la variable "magnitud" tiene que ser estática... esto en un entorno multihilo da problemas.

En cuanto a las otras dos no creo que tu implementación esté más optimizada que la proporcionada por la librería estándar, donde ha metido mano gente que desde luego era más lista que tu y que yo.

Las funciones de conversión, en vez de sumar y restar por 32 ( difícil de entender a primera vista ), sería más lógico que lo indicases como 'a' - 'A'. Es lo mismo pero la segunda opción es más gráfica y el compilador genera exactamente el mismo código al optimizar.

En el caso de las variables estáticas en las funciones de conversión te digo lo mismo, no tienen sentido y no funcionan en entornos multihilo.

Álvaro G. Tenorio

Obviamente que en la librería estándar de C y C++ han metido la mano ingenieros con mucha más preparación que yo, no pretendo superarles ni mucho menos, todavía estoy aprendiendo.

No, la función magnitudTexto(); no añade nada a la función strlen(); de string.h pero en muchas universidades (todavía no estoy en esa edad) tengo constancia de que piden a modo de ejercicio para familiarizarse con eso de los algoritmos y demás hacer implementaciones propias de algunas funciones de la librería stándar (he aquí la mía a modo de aporte).

En cuanto a las otras dos funciones son diferentes de toupper(); y tolower(); en el sentido en que estas dos funciones estándar trabajan con caracteres individuales, teniendo que hacer tú mismo el bucle correspondiente para pasar una cadena entera a mayúsculas o minúsculas. Las funciones de mi aporte no tienen sustituto en el estándar.

Una de las razones por la que me registré en el foro fue para aprender, por tanto gracias por tu consejo sobre las variables estáticas de las que no se mucho aunque tengo entendido que si uno llama en sucesivas ocasiones a una función al ser la variable estática sólo se inicializa una vez, mejorando el rendimiento del programa. Me gustaría saber más sobre el tema, al igual que de programación multihilo de la que sólo he tocado un poco en Java.

eferion

Cita de: Álvaro G. Tenorio en  2 Septiembre 2013, 20:42 PM
Obviamente que en la librería estándar de C y C++ han metido la mano ingenieros con mucha más preparación que yo, no pretendo superarles ni mucho menos, todavía estoy aprendiendo.

No, la función magnitudTexto(); no añade nada a la función strlen(); de string.h pero en muchas universidades (todavía no estoy en esa edad) tengo constancia de que piden a modo de ejercicio para familiarizarse con eso de los algoritmos y demás hacer implementaciones propias de algunas funciones de la librería stándar (he aquí la mía a modo de aporte).

No te lo tomes a mal, no te estoy quitando el mérito. Simplemente he leído tu mensaje y visto tu código y he contestado en consecuencia. No conozco los detalles particulares y personales que te han llevado a escribir el mensaje.

Cita de: Álvaro G. Tenorio en  2 Septiembre 2013, 20:42 PM
En cuanto a las otras dos funciones son diferentes de toupper(); y tolower(); en el sentido en que estas dos funciones estándar trabajan con caracteres individuales, teniendo que hacer tú mismo el bucle correspondiente para pasar una cadena entera a mayúsculas o minúsculas. Las funciones de mi aporte no tienen sustituto en el estándar.

Lo de las funciones toupper y tolower es cierto, de hecho mi único comentario acerca de tus funciones sustitutas es que poner a pelo un '32' no es la mejor opción. Eso y lo de las variables estáticas que no tiene ningún sentido.

Cita de: Álvaro G. Tenorio en  2 Septiembre 2013, 20:42 PM
Una de las razones por la que me registré en el foro fue para aprender, por tanto gracias por tu consejo sobre las variables estáticas de las que no se mucho aunque tengo entendido que si uno llama en sucesivas ocasiones a una función al ser la variable estática sólo se inicializa una vez, mejorando el rendimiento del programa. Me gustaría saber más sobre el tema, al igual que de programación multihilo de la que sólo he tocado un poco en Java.

Te explico un poco acerca de las variables estáticas.

Imagina que quieres una función que vaya sumando valores y te retorne el resultado. Una primera versión de dicha función podría quedar tal que:

int sumador(int valor)
{
    int total += valor;
    return total;
}


Al ejecutar esto te das cuenta de que SIEMPRE te va a devolver lo mismo que le pases como parámetro, no va a sumar nada y, en resumen, no funciona.

Aquí entran en juego las variables estáticas. Las variables estáticas no pierden su significado al salirse de su ámbito ( esto te toca mirarlo en internet si no sabes qué es que si no me enrollo demasiado ), sino que mantienen su identidad durante la ejecución de la aplicación.

Haciendo un par de cambios conseguimos que la función cumpla su cometido:

int sumador( int valor )
{
    static int total = 0;
    total += valor;
    return total;
}


El asignarle un valor al declarar la variable es algo que sólo se ejecutará la primera vez que se llame al código, el resto del tiempo la variable 'total' recordará el valor que se le asignó la última vez y el sumador funcionará correctamente.

El problema que tienen las variables estáticas en entornos multihilo es que si yo llamo a sumador a la vez desde dos hilos diferentes, al compartirse entre ambos el valor de la variable estática, el resultado va a ser incorrecto porque las instrucciones se van a pisar entre si.

Eternal Idol

Usar static en este caso esta mal desde el vamos, con un solo hilo es suficiente para que de resultados incorrectos. Proba a llamar a la funcion primero con la cadena "hola" y despues con la cadena "a". Lo primero que va a hacer en el segundo caso es comprobar que texto[4] (magnitud se inicializo a 0 y se incremento hasta 4 por hola y asi quedo para seguir siendo usada en la siguiente llamada) sea diferente de cero ... es decir un elemento invalido.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Álvaro G. Tenorio

No me tomé a mal tu comentario, simplemente me intenté explicar, muchas gracias por la información a cerca de las variables static, es un modificador que no suelo usar porque no se mucho de él, al fin y al cabo fallando se aprende, ya he modificado el código para solventar el error.

Por cierto, no entendí la solución que me ofreciste para lo del 32, te referías a sumar tal que así:
Código (cpp) [Seleccionar]
texto[contador] += 'A';

Pregunto.

eferion

Cita de: Álvaro G. Tenorio en  2 Septiembre 2013, 22:40 PM
No me tomé a mal tu comentario, simplemente me intenté explicar, muchas gracias por la información a cerca de las variables static, es un modificador que no suelo usar porque no se mucho de él, al fin y al cabo fallando se aprende, ya he modificado el código para solventar el error.

Por cierto, no entendí la solución que me ofreciste para lo del 32, te referías a sumar tal que así:
Código (cpp) [Seleccionar]
texto[contador] += 'A';

Pregunto.

Mas bien me refería a esto para pasar a mayúsculas:

texto[contador] -= 'a' - 'A';

y esto para pasar a minúsculas:

texto[contador] += 'a' - 'A';

La lógica que está detrás de esto es que el ordenador solo entiende de información binaria, es decir, de números. Un carácter no es más que la conversión de un número de 8 bits en un carácter imprimible. el carácter 'A' se corresponde en ascii con el número 65 y el 'a' con el 97. Si haces la resta da 32 que es el número que tú has puesto directamente en el código.

Eternal Idol

Por algo strlen devuelve un size_t y no un unsigned char, tu funcion no podra devolver el valor correcto si la cadena es mayor a 255 caracteres.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Álvaro G. Tenorio

Cita de: Eternal Idol en  2 Septiembre 2013, 22:56 PM
Por algo strlen devuelve un size_t y no un unsigned char, tu funcion no podra devolver el valor correcto si la cadena es mayor a 255 caracteres.

Claro, strlen(); pretende ser una función universal estándar, la mía es una implementación casera ya tuve en cuenta el problema del límite de almacenamiento de unsigned char, por eso el array de caracteres que se toma del usuario es de 140 huecos. (suficiente para algo rapidillo, pero modificaré el código).

Gracias por la explicación del error del 32, ahora lo corrijo.

eferion

Cita de: Álvaro G. Tenorio en  2 Septiembre 2013, 23:10 PM
Claro, strlen(); pretende ser una función universal estándar, la mía es una implementación casera ya tuve en cuenta el problema del límite de almacenamiento de unsigned char, por eso el array de caracteres que se toma del usuario es de 140 huecos. (suficiente para algo rapidillo, pero modificaré el código).

Gracias por la explicación del error del 32, ahora lo corrijo.

El problema que tiene unsigned char para cosas como estas es que si superas los 255 caracteres volverás a empezar por el cero ( 255 + 1 = 0 ) y la función no terminará nunca, entrarás en un bucle infinito.