Duda con función fgets (lenguaje C)

Cerrado
novalida - 1 dic 2008 a las 23:55
 beatriz - 6 jun 2009 a las 19:06
Hola!!!

Antes de nada dejo constancia de ser un novato y estar aprendiendo, por lo que si lo que expongo en el mensaje es muy elemental no lo hago a fin que alguien me de el código resulto, sino por intentar aprender.
Soy principiante en esto de la programación, y no sé si será una tontería la duda que tengo pero soy incapaz de encontrar el fallo en el programa que intento realizar. He buscado pro al red algún ejemplo similar y he revisado una y otra vez la sintaxis de fetgs, pero no consigo nada.
El programa: un programa que recibe un caracter y una cadena de caracteres por el teclado. La cadena de caracteres será el nombre de un archivo. El programa ha de mostrar en pantalla las líneas que contengan el caracter introducido por teclado.

El código que he realizado:

Código
#include<stdio.h>main(){ FILE *fich; char archivo[20]; char linea[150]; char caracter; int i; printf("\nIntroduzca un caracter: "); scanf("%c", &caracter); printf("Introduzca el nombre de un fichero: "); scanf("%s", archivo); printf("\n\n"); fich=fopen(archivo, "r"); if (fich==NULL) { perror("Error al abrir fichero"); } while (fgets(linea, 150, fich)!=NULL) { for (i=0; i < 150; i++) { if (linea[i]==caracter) { fputs(linea, stdout); break; } } } if (fclose(fich)!=0) { perror("Error al cerrar fichero"); } return 0;}


El contenido del fichero que intento leer es el siguiente:

Este es un ejemplo
para comprobar si el programa
es capaz de imprimir en pantalla
unicamente las palabras
que contengan el caracter
introducido por el teclado
aa
ee
ii
oo
uu


Y el resultado que obtengo al ejecutar el programa :

Introduzca un caracter: u
Introduzca el nombre de un fichero: prueba.txt

Este es un ejemplo
unicamente las palabras
que contengan el caracter
introducido por el teclado
aa
ee
ii
oo
uu


Como podeis ver el resultado no es el esperado.
Alguien me puede ayudar diciéndo qué hago mal??? Preferíría saber qué hago mal y algún consejo a que me deis el código arreglado (que sino no aprendo).


Un saludo y espero respuesta.
Muchas gracias!!!

3 respuestas

Opinio q sera algo asi lo q tendrias q hacer.... aunq aki se pasa el archivo por parametros, pero igualmente se lo puedes pasar poor pantalla. La funcion q uso en el programa igualmente se puede hacer con la de c.... Saludos

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int BuscaCaracter(char *cadena,char caracter);

main(int argc,char *argv[])
{
FILE *Fichero;
int Devuelve=0,cont=0;
char CadAux[80];

if(argc!=3) /*Comprueba si el número de argumentos que pasamos es correcto.*/
{
printf("\nDebes introducir el nombre del archivo (espacio) y el caracter a buscar.");
getchar();
exit(1);
}

if((Fichero=fopen(argv[1],"r"))==NULL) /*Abre el fichero para lectura*/
{
printf("\nERROR. El fichero %s no se puede abrir o no existe.",argv[1]);
getchar();
exit(1);
}

if(strlen(argv[2])>1) /*Si argv[2] es mayor de 1 es que se ha introducido mas de un caracter*/
{
printf("\nERROR. Has introducido mas de un caracter.");
getchar();
exit(1);
}

fgets(CadAux,80,Fichero);
gotoxy(10,2);
printf("El caracter %s se encuentra en la LINEA:",argv[2]);
while(!feof(Fichero))
{
cont++;
if(BuscaCaracter(CadAux,*argv[2])) /*Es lo mismo que (llamada funcion)==1*/
{
gotoxy(2,5+I);
printf("%d. %s",cont,CadAux);
Devuelve=1;
I++;
}
fgets(CadAux,80,Fichero);
}

if(Devuelve==0)
{
gotoxy(10,10);
printf("El caracter %s no se encuentra en el archivo %s.",argv[2],argv[1]);
}

fclose(Fichero);
getchar();
}


/*Si el caracter se encuentra en la cadena devuelve 1, en caso contrario devuelve 0.*/
int BuscaCaracter(char *Cadena,char Carac)
{
int Sw=0;

if(*Cadena==Carac)
Sw=1;

while(*Cadena!='\0' && *Cadena!=Carac)
{
Cadena++;
if(*Cadena==Carac)
Sw=1;
}
return Sw;
}
3
prueba con el contenido del archivo..

ii
aa
ee
oo
uu
Este es un ejemplo
unicamente las palabras
introducido por el teclado
que contengan el caracter


pd: no todas las lineas leidas del archivos seran de 150 caracteres.
1
lllllllllllllllllllllllllllllllllllll
20 may 2009 a las 22:14
che gonzi sos gonzi alvarez?
0
Gracias por contestar gonzi. Al final conseguí lo que pretendía.
El problema que tenía era que no contaba con el caracter salto de línea, ya que usando fgets suponía que el programa entendería lo que yo quería. No obstante al usar un vector con no todos los "espacios" ocupados por caracteres resulta un poco inesperado su comportamiento al forzar la lectura de todos esos espacios.
El problema que surgía aparecía al forzar la lectura de todos lo elementos del vector, de modo que con el uso de dos condiciones dentro del bucle que recorre dicho vector lo he solucionado.

PRIMERA CONDICIÓN

Busca en la cadena el caracter, se imprime la cadena (con el uso de fgets se impimirá la línea, que era lo que se pretendía con el programa) y sale del bucle que recorre el vector (por eso el uso de break).

SEGUNDA CONDICIÓN

Si no encuentra el caracter y encuentra el salto de línea ('\n') sale del bucle que recorre el vector sin imprimir nada (y así ningún otro caracter ajeno a nuestro propósito).

CONCLUSIÓN
El problema que tenía era bastante sencillo, no obstante por falta de experiencia a un novato como es mi caso le puede resutlar difícil y no tan obvio como pueda resultar a primera vista.
El uso de break, aunque por principios de programación estructurada se limite su utilización, finalmente en este código, es el correcto, a pesar de ser el código menos ortodoxo que con el uso de la función strchr.

El código quedaría de la siguiente manera:


#include<stdio.h>
main()
{
FILE *fich;
char archivo[20];
char linea[800];
char caracter;
int i;

printf("\nIntroduzca un caracter: ");
scanf("%c", &caracter);
printf("Introduzca el nombre de un fichero: ");
scanf("%s", archivo);
printf("\n\n");

fich=fopen(archivo, "r");
if (fich==NULL) {
perror("Error al abrir fichero");
}

while (fgets(linea, 800, fich)!=NULL)
{
for (i=0; i < 800; i++) {
if (linea[i]==caracter) {
fputs(linea, stdout);
break;
}
else if (linea[i]=='\n') {
break;
}
}
}


if (fclose(fich)!=0) {
perror("Error al cerrar fichero");
}

return 0;

}



Espero que pueda ayudar alguien.
Si el tema es necesario marcar como solucionado, por favor, hacedme saber cómo debo hacerlo.

Un saludo a todos.
1
No fuerzas a la funcion fgets a leer hasta llenar hasta los 150 caracteres.
sino que la funcion fgets lee la linea del archivo hasta el salto de linea (una linea leida) ocupando los espacios necesarios, pero no limpia los que no se usan, si en la primera linea lees los 100 caracteres por ejemplo y en la siguiente lees 10 caracteres, pues el resto de espacios no usado en la lectura mantendra el contenido de la lectura anterior.

ej.
linea[12]

primera lectura aaaaaaaaaa\n
segunda lectura bbb\naaaaaa\n
....

sigue esforzandote...
0
novalida > gonzi
4 dic 2008 a las 23:12
Sí bueno, era eso más o menos lo que quería decir. Al no usar todos los "bloques" del array su funcionamiento puede resultar inesperado. Gracias por la ayuda :)
Saudos!!!
0
novalida > novalida
4 dic 2008 a las 23:25
por cierto, quizás sea una pregunta absurda (es que estoy empezando y no sé demasiado :S) pero me gustaría saber si se puede usar memoria dinámica para cualquier variable, por ejemplo, para un programa que únicamente tenga un array que almacene el nombre de una persona. el cual se introduce por teclado:
Por ejemplo en este miniprograma:

#include<stdio.h>
main()
{
char nombre[20];
scanf("%s", nombre);
printf("%s", nombre);
return 0;
}

Según está el código se guardaría memoria para 20 caracteres, indemoendientemente de los que se usasen. Sé que se utiliza la función malloc (y más tarde free para liberar la memoria), pero en este caso tan básico no sabría hacerlo.
0
TIMINeutron > novalida
6 ene 2009 a las 20:24
Bueno, como primera medida, debes saber que siempre que trabajes con memoria dinámica la cosa se comienza a complicar (seguro que lo intuías XD). 

1 - Como primer paso, declaras una variable "fija" del tipo que desees, y le pones una longitud de array que seguramente sobrará (p.e: si guardarás un nombre, con un char aux_nombre[20] - como has hecho - está más que bien). 

        char aux_nombre[20];

2 - Bien, seguidamente lo que debes hacer para reservar memoria es declarar un puntero del tipo del que quieras hacer la reserva (p.e: Si tienes un struct informacion delcarado, debes declarar un puntero del tipo struct informacion* info). Si tienes problemas con este punto, hazmelo saber. 

        char *nombre;

3 - OK, después de tener el puntero, lo que haces es introducir por teclado el nombre y lo guardas en la variable auxiliar que has construido.

        printf("Introduzca el nombre: ");
        scanf("%s",&aux_nombre);       //OJO no te dejes el '&' que veo que te dejaste cuando preguntaste 
                                                    //(seguro es un descuido - :P).

4 - Ahora vamos a usar un poco una de las librerías que los "cerebros" esos han inventado. La incluyes como string.h (#include <string.h>). OK, dentro de esta librería hay una función llamada strlen. Esta función te devuelve el numero total de caracteres SIN CONTAR EL CARACTER DE FINAL DE CADENA '\0'. El paso de parametros es un char, osea en este caso, aux_nombre.

        strlen(aux_nombre);

5 - Bien, ahora lo unico que nos falta ya es reservar el espacio de memoria. Bien, para esto existe la función malloc(), cuyo unico parametro de entrada es la cantidad de memoria que queremos reservar, y que devuelve la dirección del primer clúster de memoria ocupado (ya se que no son clústers, pero bue...). Bien, para utilizar esta función, debes saber solo una cosa más. Como normalmente no se sabe la longitud unitaria (de uno solo) de un char, un int, o peor aun, una estructura personalizada (un struct{}), existe una funcion en C que lo que hace es devolverte el espacio de memoria que ocupa el tipo de variable que le introduzcas, y es la funcion sizeof(). Su utilización es muy simple, se le entra un único parámetro, que es el tipo, y devuelve la cantidad de memoria necesaria.

        sizeof(char);

6 - Bueno, como último paso, unimos los pasos 4 y 5, les ponemos un poco de magia, de esa que solo un ordenador tiene (XD), y armamos nuestro malloc:

        nombre=(char*)malloc(sizeof(char)*strlen(aux_nombre));

     Explicacion: Como la función entera nos devuelve una dirección, se la asignamos a nuestro puntero de inicio de la cadena. El (char*) es porque debes indicar de que tipo de puntero se trata el que estas creando. malloc es la función, y su parametro de entrada es (sizeof(char)*strlen(aux_nombre)). 
Este parametro nos da la memoria necesaria para un 'char' (el sizeof(char)), multiplicada por la cantidad de 'chars' que tenemos (el strlen(aux_nombre)). Con todo esto tenemos nuestra reserva de memoria terminada.
Bien nunca te tienes que olvidar de liberar la memoria reservada manualmente. Normalmente cuando se declara una variable 'fija' el compilador añade al .exe las instrucciones necesarias para liberar la memoria que ha reservado, pero si lo haces manualmente, manualmente tienes que liberarlo. Para eso se usa un free(), cuyo parametro de entrada es EL PUNTERO de inicio de nuestro bloque de memoria (en nuestro caso nombre - OJO, NO *nombre !).

        free(nombre).

Por ultimo, el codigo completo quedaría de la siguiente manera:


#include <stdio.h>
#include <string.h>

void main (void)
{
     char aux_nombre[20];
     char* nombre;

     printf("Introduzca el nombre: ");
     scanf("%s",aux_nombre);
     printf("\n\n");

     nombre=(char*)malloc(sizeof(char)*strlen(aux_nombre));
     if(nombre==NULL)                                                                           //Estas líneas adicionales son porque
     {                                                                                                   //la funcion malloc() devuelve NULL si 
          printf("ERROR - No se ha podido reservar la memoria dinámica.\n");    //no se ha podido reservar la memoria,
     }                                                                                                   //y lo arovechas para un mensajito - XD
 
     //...//        trozo de código donde haces lo que quieras con la cadena.

     free(nombre);
}

Lo ultimo que me dejo es que la nueva cadena se opera de igual manera que las demás. Quiero decir, que si el nombre introducido es Juan, y recuperas el valor nombre[2], obtendrás el caracter 'a' como respuesta. Muy bien, eso es todo, y es válido para cualquier reserva de vector que quieras hacer. Si además te interesa tener una matriz bidimensional con muchos nombres, la funcion cambia un poco, pero en esencia es lo mismo. Espero haberte sido de ayuda, y cualquier cosa sigue el hilo. Saludos.

Timoteo Frascaroli

----

Que satisfacción cuando las cosas te salen bien.
0