PRACTICA 7: EJERCICIOS RESUELTOS
Ejercicio 10: Programar funciones análogas a strlen(), strcpy(), strcat() y strcmp().
Ejercicio 1: Leer una palabra y escribirla al revés.Ejercicio 2: Leer una frase (línea) y escribirla al revés.
Ejercicio 3: Transformar un texto.
Ejercicio 4: Modificar el Ejercicio 3.
Ejercicio 5: Ordenar alfabéticamente.
Ejercicio 6: Modificar el Ejercicio 5.
Ejercicio 7: Recuento de caracteres de un fichero.
Ejercicio 8: Modificar el Ejercicio 7 para contar las palabras de un fichero.
Ejercicio 9: Un fichero secreto.
Ejercicio 10: Programar funciones análogas a strlen(), strcpy(), strcat() y strcmp().
Ejercicio 1: Leer una palabra y escribirla al revés. |
Guarda este programa como alreves.c.
Solución comentada del Ejercicio 1.
Se presenta a continuación el programa alreves.c que lee una palabra del teclado, la almacena en una array de 20 posiciones y luego la escribe al reves.
/* fichero alreves.c */ /* Este programa lee una palabra y la escribe al revés */ #include <stdio.h> void main (void) { char c, palabra[21]; int i; printf("Teclee una palabra de menos de 20 letras:\n"); scanf("%s", palabra); i = 0; while(palabra[i++] != '\0') ; printf("%s tiene %d letras.\n", palabra, i); printf("%s escrita al revés es: ", palabra); while (i >= 0) printf("%c", palabra[i--]); }Comentario: La forma de almacenar el array leyéndolo del teclado es mediante la función scanf(). Luego, mediante un bucle while se determina el número de letras. La forma de chequear la condición de final del bucle es por reconocimiento del carácter \0 que tienen todas las cadenas de caracteres al final.
La cadena se podría haber rellenado de otra manera, como se verá en el Ejercicio siguiente. También el número de caracteres de la cadena se puede determinar mediante otra forma: llamando a la función de libreria strlen(char*).
Una vez que ya conocemos el número de caracteres de la cadena, no tenemos más que escribir el array al revés, con un bucle con un contador decreciente.
Ejercicio 2: Leer una frase (línea) y escribirla al revés. |
Te sugerimos que pruebes el programa con una de las siguientes frases (llamadas palíndromos): "dabale arroz a la zorra el abad"; "a ti no bonita". Guarda el programa con el nombre frase.c.
Solución comentada del Ejercicio 2.
Se presenta a continuación una posible solución al Ejercicio 2. En esta solución se va a presentar otra forma de leer un array del teclado. La solución más sencilla a este ejercicio hubiera sido sustituir la línea scanf(" %s", palabra); del programa alreves.c por scanf(" %[^\n]", frase);
/* fichero frase.c */ #include <stdio.h> void main(void) { char c, frase[100]; int i, n; printf("Introduzca una frase de menos de 100 letras.\n"); printf("Para finalizar pulse ^Z:\n"); i = 0; while((c=getchar())!=EOF) { frase[i] = c; i++; } frase[i] = '\0'; printf("\n"); for (n=i; n>=0; n--) putchar(frase[n]); printf("\n"); }Comentario: En este caso, la lectura del array se realiza mediante un bucle while cuya condición de ejecución es la de encontrar un carácter distinto del carácter fin de fichero (EOF). Cuando en vez de leer la frase desde un fichero, ésta se introduce desde teclado, el carácter equivalente al EOF es control-z, que también se suele denotar como ^z.
Una vez almacenada la frase en el array, se procede a escribirla al revés de la misma forma que se hacía en el programa anterior. En este caso se ha utilizado la macro de librería putchar(c); en lugar de printf(), de modo que la frase se escribe carácter a carácter con dicha macro.
Ejercicio 3: Transformar un texto. |
En C se utiliza la constante simbólica EOF para indicar el final de un fichero (esta constante está definida en el fichero stdio.h). Su valor numérico es (-1) y equivale también a <control>z (^z) cuando se introduce el texto desde teclado.
Solución comentada del Ejercicio 3.
Se presenta a continuación el listado correspondiente al Ejercicio 3 tal y como se presentó en el enunciado de la práctica.
/* fichero inverso.c */ /* Este programa convierte las mayusculas en minusculas y viceversa */ /* y escribe el texto cambiado al reves */ #include <stdio.h> void main(void) { int ch; char texto[100]; /* limite de caracteres del texto */ int i, n, dif; dif = 'a'-'A'; printf("Introduzca un texto.\n"); printf("Pulse ^Z para finalizar:\n"); i = 0; while ((ch = getchar()) != EOF) { if ((ch>='a')&&(ch<='z')) ch -= dif; else if ((ch>='A')&&(ch<='Z')) ch += dif; texto[i++] = ch; } texto[i] = '\0'; for (n=i; n>=0; n--) printf("%c", texto[n]); }Comentario: En este programa se utiliza la forma de introducir la cadena como ya se hizo en el programa frase.c. Para cambiar las mayúsculas a minúsculas y viceversa hay que chequear el carácter y ver si es alfabético o no. Una vez que se sabe que es un carácter alfabético, hay que saber que el código ASCII correspondiente a la "A" es el 65 y el correpondiente a la "a" es el 97, es decir, la diferencia entre ambos es 32.
En lugar de teclear el texto cada vez, crea un fichero de texto llamado texto1 y redirecciona la entrada con el operador (<) cuando vayas a ejecutar el programa, de la siguiente manera:
G:\a63876.07>inverso < texto1
Ejercicio 4: Modificar el Ejercicio 3. |
Solución comentada del Ejercicio 4.
/* fichero invers2.c */ #include <stdio.h> #include <stdlib.h> void main(void) { int ch; char texto[100]; int i, n, dif; FILE *fi; fi = fopen("texto.d","r+"); dif = 'a' - 'A'; i = 0; while ((ch = getc(fi)) != EOF) { if ((ch >= 'a')&&(ch <= 'z')) ch -= dif; else if ((ch >= 'A')&& ch <= 'Z')) ch += dif; texto[i] = ch; i++; } texto[i] = '\0'; for (n=i; n>=0; n--) putc(texto[n], fi); fclose(fi); }Comentario: El programa es idéntico al anterior con la salvedad del manejo de ficheros. Hay que declarar un puntero al fichero mediante la declaración FILE *fi;. Este puntero se utilizará a la largo del programa para designar el fichero. En primer lugar, este fichero hay que abrirlo mediante la instrucción fopen, en la que se definen el nombre del fichero y el tipo. En el resto del programa lo único que cambia son las instrucciones de entrada y salida: putc sustituye a putchar y getc sustituye a getchar.
Ejercicio 5: Ordenar alfabéticamente. |
Pensemos por un momento en el objetivo de este programa: queremos que lea diez palabras, las almacene y pueda compararlas para ordenarlas. ¿Dónde podemos almacenar una palabra? Obviamente en un array de caracteres. ¿Y diez palabras? Podríamos utilizar diez arrays, pero ya que queremos relacionarlas entre sí resulta mucho más útil emplear una matriz de caracteres, que no es más que un array de arrays. Cada palabra se almacenaría en una fila de la matriz. Ahora bien, no todas las palabras tendrán el mismo número de letras, lo que nos hace intuir que no hay por qué reservar espacio para una matriz rectangular, sino para una matriz de diez filas donde cada una de ellas tendrá las posiciones de memoria necesarias para almacenar todos los caracteres que componen la palabra más uno (para el carácter '\0'). Volviendo a insistir: sabemos que necesitamos una matriz de 10 filas pero de número de columnas variable. El procedimiento es muy sencillo: leemos la palabra, vemos cuánto ocupa y entonces reservamos el espacio necesario. La palabra leída se almacena temporalmente en una variable array auxiliar que hemos llamado temp[], que permite almacenar palabras de hasta veinte letras.
Veamos qué hacen exactamente las siguientes intrucciones del programa:
char **cadena;Esta intrucción declara cadena como un doble puntero ( o puntero a puntero).
cadena = malloc(10*sizeof(char*));Esta intrucción reserva memoria para un vector de punteros, llamado también cadena[ ]. Este vector contiene diez posiciones de memoria, cada una de las cuales contiene un puntero a char. La función malloc(), como se recordará, tiene como valor de retorno un puntero al primer elemento de la zona reservada y se almacena en cadena que ya se declaró en la instrucción antes comentada. Por lo tanto cadena apunta a cadena[0], que a su vez apuntará luego al primer carácter de la primera palabra.
Vector de punteros :
scanf("%s", temp);
Esta intrucción lee una palabra y la almacena en el array temp[
]. cadena[i] = malloc((strlen(temp)+1)*sizeof(char));Esta instrucción reserva la memoria necesaria, en bytes, para la palabra almacenada en temp[ ] (incluyendo el carácter '\0'). El valor de retorno, que es la dirección del primer carácter de la palabra, se almacena en el puntero cadena[i].
Sólo nos queda por comentar que el algoritmo que se sigue para ordenar las palabras es análogo al que se utilizó para ordenar un conjunto de números.
Solución comentada al Ejercicio 1.
/* fichero ordena.c */ /* Este programa pide diez palabras y las ordena por orden alfabetico */ #include <stdio.h> #include <stdlib.h> void main(void) { char **cadena; /* declaración de puntero a matriz de caracteres */ int i, j; int n; char temp[20]; /* declaración del array auxiliar donde almacenaremos temporalmente cada palabra */ char *aux; /* decalaracion de puntero a carácter, auxiliar */ printf("%s%s\n", "Este programa ordena diez palabras", "introducidas por teclado."); printf("Introduzca las diez palabras:\n"); cadena = malloc(10*sizeof(char*)); for (i=0; i<10; i++) { printf("Palabra %d: ", i+1); scanf("%s", temp); cadena[i] = malloc((strlen(temp)+1)*sizeof(char)); strcpy(cadena[i], temp); } for (i=0; i<9; i++) { for(j=i+1; j<10; j++) { if ((strcmp(cadena[i], cadena[j]))>0) { aux = cadena[i]; cadena[i] = cadena[j]; cadena[j] = aux; } } } printf("La cadena ordenada es:\n"); for (i=0; i<10; i++) printf("%s\n", cadena[i]); }Comentario: Este programa tiene un gran interés, por realizar reserva dinámica de memoria. Vamoa a reservar espacio para 10 palabras, que pueden ser de longitud diferente. Por eso se lee primero la palabra, se ve qué longitud tiene con la función strlen(), y entonces se le reserva memoria con un espacio adicional para el carácter fin de cadena. La cadena temp[ ] se utiliza para almecenar las palabras nada más leerlas, cuando todavía no se sabe su número de letras.
El método de ordenación utilizado es el mismo que se utilizó en una práctica anterior para ordenar números. La comparación se hace por medio de la función strcmp(), que devuelve cero si las cadenas son iguales, y un número positivo o negativo según la primera cadena sea alfabéticamente posterior o anterior a la segunda cadena, respectivamente. Otro punto importante es que no se permutan las palabras, sino los punteros que apuntan a su primera letra.
Ejercicio 6: Modificar el Ejercicio 5. |
Solución comentada del Ejercicio 6.
/* fichero ordena2.c */ #include <stdio.h> #include <stdlib.h> void main(void) { char **cadena, *aux; int i, j, n; char temp[20]; printf("%s%s\n", "Este programa ordena diez nombres", "introducidos por teclado."); printf("Introduzca los diez nombres:\n"); cadena = (char**)malloc(10*sizeof(char*)); for (i=0; i<10; i++) { printf("Nombre %d: ", i+1); scanf(" %[^\n]", temp); cadena[i] = (char*)malloc((strlen(temp)+1)*sizeof(char)); strcpy(cadena[i], temp); } for (i=0; i<9; i++) for (j=i+1; j<10; j++) if ((strcmp(cadena[i], cadena[j])) > 0) { aux = cadena[i]; cadena[i] = cadena[j]; cadena[j] = aux; } printf("La lista ordenada es:\n"); for (i=0; i<10 ; i++) printf("%s\n", cadena[i]); }Comentario: La única diferencia entre este ejercicio y el anterior reside en la forma de introducir la cadena de caracteres: En el ejercicio anterior se utilizó scanf("%s", temp), pero esta forma de llamar a la función se detiene al encontrar un blanco, un \t o un \n. Por ello, nn este ejercicio se emplea la función scanf(" %[^\n]", temp), que se para sólo cuando llega al carácter \n.
Ejercicio 7: Recuento de caracteres de un fichero. |
Solución comentada al Ejercicio 7.
/* fichero recuento.c */ /* Este programa cuenta caracteres de un fichero */ #include <stdio.h> void main(void) { int n_blancos = 0, c, n_digitos = 0; int n_letras = 0, n_nulineas = 0, n_otros = 0; while ((c = getchar()) != EOF) if (c == ' ') ++n_blancos; else if (c>='0' && c<='9') ++n_digitos; else if (c>='a' && c<='z' || c>='A' && c<='Z') ++n_letras; else if ( c == '\n') ++n_nulineas; else ++n_otros; printf("%10s%10s%10s%10s%10s%10s\n\n", "blancos", "digitos", "letras", "lineas", "otros", "total"); printf("%10d%10d%10d%10d%10d%10d\n\n", n_blancos, n_digitos, n_letras, n_nuli, n_otros, n_blancos + n_digitos + n_letras + n_nulineas + n_otros); }
Ejercicio 8: Modificar el Ejercicio 7 para contar las palabras de un fichero. |
Para contar las palabras, se puede suponer en principio que los separadores de palabras son un único espacio en blanco, o un único tabulador, o un único salto de línea. El programa irá leyendo carácter a carácter e incrementará en una unidad una variable cada vez que se encuentre una letra precedida por uno de estos caracteres. Hay que tener en cuenta que la primera palabra del fichero puede no estar precedida por alguno de estos caracteres.
Puedes desarrollar una versión más complicada de este programa, que tenga en cuenta que entre cada dos palabras puede haber uno o más espacios en blanco, o un espacio en blanco y un tabulador, o -en general- cualquier combinación de espacios en blanco, tabuladores y saltos de línea. Para ayudarte en la realización de este programa tan difícil, te proponemos el siguiente algoritmo:
while (caracter leido distinto de EOF) do {
if (estoy dentro de una palabra)
then [ if (encuentro un separador) then (salgo fuera)
else (no hago nada) ]
else if [(encuentro separador) then (no hago nada)
else (entro dentro de una palabra, palabra++)]
}
Construye tu propio fichero de comprobación de este programa
que contenga un mínimo de 20 palabras en un mínimo de 4 líneas.
Deberá contener también algún tabulador. Esta fichero
se llamará palabras. Solución comentada del Ejercicio 8.
El listado de éste programa es como sigue:
/* fichero contador.c */ #include <stdio.h> void main(void) { char c; int pass, num_pal; num_pal = 0; pass = 0; printf("Introduzca un texto.\n"); printf("Pulse ^Z para finalizar:\n"); while( (c = getchar()) != EOF ) { if ( ((c >= 'a')&&(c <= 'z')) || ((c >= 'A')&&(c <= 'Z')) ) { if (pass == 0) { num_pal += 1; pass = 1; } } else if (pass == 1) pass = 0; } printf("El n£mero de palabras del texto es %d\n", num_pal); }Comentario: La dificultad de este programa reside en la confección del algoritmo.
Para contar las palabras se define una variable llamada num_pal que indicará el número de palabras que se van contando. Se define también otra variable que se llama pass que indica si nos encontramos dentro o fuera de una palabra; si esta variable vale 1 nos encontramos dentro y si vale cero nos encontramos fuera. El algoritmo que sigue este programa es:
1 Leo el carácter
2 Si el carácter es alfabético, se chequea si estoy dentro o fuera de la palabra (pass = 1 ó pass = 0).
2.1 Si pass == 0 significa que estaba fuera de palabra y he entrado en una, por lo que hacemos num_pal += 1 (incrementamos una palabra en el contador) y pass = 1 (estamos dentro de palabra).
2.2 Si pass == 1 significa que estábamos metidos en una palabra y que seguimos dentro de ella. No hacemos nada.
3 Si el carácter no es alfabético, chequeo si he salido o no de una palabra (chequeo pass).
3.1 SI pass== 1 significa que el carácter anterior era alfabético y, por tanto, he salido de una palabra: hacemos pass = 0 porque ahora nos hemos salido de ella.
3.2 Si pass == 0 significa que no estábamos en ninguna palabra y que seguimos sin estar, por tanto no hacemos nada.
Ejercicio 9: Un fichero secreto. |
Solución comentada al Ejercicio 9.
/* fichero secreto.c */ /* Este programa encripta un texto */ #include <stdio.h> void main(void) { char ch; int n; printf("Introduzca la clave: "); scanf("%d", &n); getchar(); printf("Introduzca los caracteres.\n"); printf("Pulse ^Z para finalizar:\n"); while((ch = getchar()) != EOF) if (ch == '\n') printf("%c", ch); else printf("%c", ch+n); }
Ejercicio 10: Programar funciones análogas a strlen(), strcpy(), strcat() y strcmp(). |
strlen( ) cuenta_caracteres( )
strcpy( ) copia_cadenas( )
strcat( ) concatena_cadenas( )
strcmp( ) compara_cadenas( )
Guarda estas funciones en un fichero análogo a string.h y al que llamarás misfunc.h e incluye en el encabezamiento del programa principal la instrucción:
#include "misfunc.h"El programa principal se llamará cadenas.c.
Solución comentada del Ejercicio 10.
/* fichero cadenas.c */ #include <stdio.h> #include "misfunc.h" unsigned cuenta_caracteres(char*); char* copia_cadenas(char*,char*); char* concatena_cadenas(char*,char*); int compara_cadenas(char*,char*); void main(void) { char car1[100] = "Esto es una cadena de caracteres"; char car3[100] = "Esto es una cadena de caracteres"; char car2[100]; printf("%d\n", cuenta_caracteres(car1)); copia_cadenas(car1,car2); printf("%s\n",car2); printf("%s\n",concatena_cadenas(car1,car2)); printf("%d\n",compara_cadenas(car1,car2)); printf("%d\n",compara_cadenas(car2,car3)); } unsigned cuenta_caracteres(char* carac) { unsigned i=0; while (carac[i] != '\0') i++; return i; }Comentario: La función cuenta_caracteres() cuenta los caracteres de la cadena detectando el carácter o marca de final de cadena. Por lo demás, esta función es muy sencilla.
char* copia_cadenas(char* carac1, char* carac2)
{
int i=0;
while ((carac2[i]=carac1[i]) != '\0')
i++;
return carac1;
}
Comentario: La función copia_cadenas()
anterior tiene como punto interesante el copiar el carácter antes
de compararlo con la marca de fin de cadena. Para ello la asignación
debe ir entre paréntesis, de modo que se realice antes de comparar
con el carácter de fin de cadena. De este modo se asugura que dicho
carácter es también copiado por la función. char* concatena_cadenas(char* carac1, char* carac2)
{
int i=0, j=0;
while (carac1[i++] != '\0')
;
while (carac2[j] != '\0')
carac1[i++] = carac2[j++];
carac1[i] = '\0';
return carac1 ;
}
Comentario: Se detecta primero el final de la cadana carac1[
]. Después se van copiando a continuación todos los
caracteres de carac2[ ] y finalmente se añade el carácter
fin de cadena. int compara_cadenas(char* carac1, char* carac2)
{
int i = 0, dif;
while (carac1[i] != '\0') {
dif = carac1[i] - carac2[i];
if (dif == 0)
i++;
else
return (dif);
}
if (carac2[i] == '\0')
return(0);
}
Comentario: Para comparar las dos cadenas, lo que hacemos
es recorrer la primera de ellas (carac1), hasta que encontramos
el caracter \0. Dentro de este bucle lo que hacemos es restar
posiciones equivalentes de las dos cadenas. Recuérdese que un carácter
se puede manejar como carácter o se puede considerar su equivalencia
en ASCII. Así, si restamos los dos caracteres, lo que en realidad
estamos haciendo es restar su equivalente en ASCII. Por tanto, si la diferencia
entre los dos caracteres es cero, significa que son iguales. El bucle continua
hasta que encuentra dos caracteres distintos con lo cual no se cumple la
sentencia if ( dif == 0) y se ejecuta return(dif);
con lo cual se devuelve un valor distinto de cero y, por tanto, las cadenas
son distintas. Si se llega al final de la primera cadena y en esa posición
se tiene que la segunda cadena acaba también (carac2[i] =
'\0') es que las cadenas son iguales y se devuelve un cero.
No hay comentarios:
Publicar un comentario