PRACTICA 7: EJERCICIOS RESUELTOS

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.

PrevioSiguienteTop

Para leer una palabra, contar las letras y escribirla al revés, lo primero que hay que hacer es pedir la palabra y almacenarla. Una vez que tenemos la palabra en un array, para contar el número de letras se chequea letra por letra (bucle while) sumando 1 cada vez. Este bucle finaliza cuando el carácter leído es '\0' que indica el final de la palabra. Este carácter, como ya se ha dicho antes, es introducido por la función scanf( ) de manera automática. Para escribir la palabra al revés, basta con leerla empleando un bucle que cuente hacia atrá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.

PrevioSiguienteTop

Basándote en el programa anterior, realiza un programa que lea una línea y a continuación la escriba al revés. La diferencia principal con el Ejercicio anterior está en que una línea puede contener varias palabras. La función scanf(), tal como se ha utilizado en el Ejercicio anterior, sólo lee la primera palabra de la frase, pues la lectura se detiene al llegar al primer carácter en blanco. En los Apuntes de C se sugieren dos formas de leer líneas completas, una con la función scanf() y otra con la macro getchar(). Puedes utilizar cualquiera de las dos.

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.

PrevioSiguienteTop

El siguiente programa lee un texto cualquiera y lo escribe al revés transformando las mayúsculas en minúsculas y viceversa. Para esto último basta tener en cuenta que la diferencia entre el código ASCII de una letra mayúscula y el de la misma letra en minúscula es un número constante. Hay que tener cuidado para no modificar los caracteres de estilo tales como espacio en blanco (' '), tabulador ('\t'), coma, ... Guarda el programa como inverso.c.

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.

PrevioSiguienteTop

Modifica el programa anterior de manera que el texto original se lea del fichero texto2, que inicialmente será una copia del fichero texto1 utilizado en el Ejercicio anterior. Para leer carácter a carácter puedes utilizar la función getf() y, una vez modificado el texto se puede escriber mediante putf() o fprintf(), a continuación del texto original, en el mismo fichero texto2. Llama a este programa invers2.c.

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.

PrevioSiguienteTop

El siguiente programa pide diez palabras y las ordena alfabéticamente. Guárdalo como ordena.c. La gran novedad que presenta este programa es la reserva dinámica de memoria mediante la función malloc().

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.

PrevioSiguienteTop

Modifica el programa anterior de manera que ordene alfabéticamente diez nombres completos de personas compuestos de primer apellido, segundo apellido y nombre. La diferencia principal está en leer líneas completas en lugar de palabras.

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.

PrevioSiguienteTop

El siguiente programa lee un texto y cuenta cuántos espacios en blanco, dígitos, letras, cambios de línea y otro tipo de caracteres contiene. Para ello se emplean una serie de condiciones comparando el carácter leído con uno de los caracteres anteriores. Si es igual a alguno de ellos se incrementa en uno el valor de una variable que se emplea como contador. Guarda el programa con el nombre recuento.c.


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.

PrevioSiguienteTop

Se trata de realizar un programa que cuente el número de palabras que contiene un fichero. El programa se llamará contador.c y deberá leer el contenido del fichero (redireccionando la entrada con el operador (<)) y mostrar por pantalla el número de palabras encontrado.

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.

PrevioSiguienteTop

El siguiente programa lee un texto y lo escribe en clave. Escribirlo en clave no es más que sumar una cantidad al número en código ASCII que corresponde a cada carácter. Esta cantidad o clave se ha de teclear en cada ejecución del programa.


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().

PrevioSiguienteTop

Realiza una programa principal que utilice varias cadenas de caracteres y llame a unas funciones programadas por ti que realicen las mismas tareas que strlen( ), strcpy( ), strcat( ) strcmp( ). Deberás llamar a tus funciones con un nombre distinto a las que existen en la librería de C. Te sugerimos los siguientes nombres:

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.