problema con archivos

Iniciado por m@o_614, 1 Octubre 2013, 20:22 PM

0 Miembros y 1 Visitante están viendo este tema.

m@o_614

Saludos tengo el siguiente programa que me lee un archivo que tiene nombres de personas separados por una coma y me pide que le ingrese un nombre y al final me tiene que imprimir el numero de veces que aparece ese nombre en el archivo solo que no me imprime la cantidad correcta

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM 10

int main()
{
   FILE *fd;
   int i = 0,j = 0,tam;
   char captura[TAM],*cadena,*nombre,car;
   if((fd = fopen("F:\\nombre_archivo.txt","r"))!= NULL)
   {
       printf("Capture nombre a buscar: ");
       fgets(captura,TAM,stdin);
       tam = strlen(captura);
       nombre = (char*)malloc((tam+1)*sizeof(char));
       cadena = (char*)malloc((tam+1)*sizeof(char));
       strcpy(nombre,captura);
       while((car  = fgetc(fd))!=EOF)
       {
           while((car = fgetc(fd))!= ',')
           {
               cadena[j] = car;
               j++;
           }
           j = 0;
           if((strcmp(cadena,nombre)) == 0)
              i++;
       }
       printf("El nombre %s se encuentra %d veces en el archivo\n",nombre,i);
   }
   else
      printf("No se pudo abrir archivo");
   return 0;
}


gracias de antemano

eferion


while((car = fgetc(fd))!= ',')
            {
                cadena[j] = car;
                j++;
            }
            j = 0;
            if((strcmp(cadena,nombre)) == 0)
               i++;


Como bien sabrás, las cadenas tienen que terminar en '\0'... ahí no estás poniendo dicho caracter, luego a saber qué es lo que estás comparando.

Un ejemplo:

nombres: pablo,antonio,pablo
nombre buscado: pablo

primer nombre:
* cadena = pabloS$FG´}S¿S-er
* encuentra el nombre: no
* motivo: cadena no está inicializada y tiene basura

Al encontrar una coma sería altamente recomendable introducir un nulo en la cadena para tener un nombre con sentido.

Además es posible que el último nombre no termine en coma, luego el algoritmo te puede fallar si el último nombre del archivo es el buscado.

Adicionalmente, el bucle para localizar las comas lo puedes eliminar si haces uso de la función strtok.

Un saludo.

m@o_614

Saludos eferion gracias por tu respuesta, a que te refieres con que la cadena no esta inicializada?? yo tenia entendido que en el momento de recorrer el archivo y asignarle a cadena[j] = car; caracter por caracter eso ya era inicializarla

eferion


char cadena[10];
cadena[ 0 ] = 'A';
printf( "%s", cadena );


Puedes ejecutar este código unas 200 veces que las 200 va a fallar el programa, bien con una violación de segmento, bien mostrando un montón de ***** en pantalla.

Cuando el programa reserva memoria ( de forma automática o tú con malloc ), ésta no se inicializa de forma automática... tiene basura, que no es otra cosa que información aleatoria ( si esa memoria no ha sido utilizada desde que se arrancó el equipo ) u obsoleta ( si esa memoria ha sido utilizada por otro programa que la ha modificado para sus usos y después la ha liberado ). En cualquier caso no es información útil para ti.

Si en el ejemplo de arriba tu quieres que el printf saque por pantalla símplemente la A tienes que asegurarte de finalizar la cadena. En c las cadenas de texto se finalizan con un caracter nulo, luego la forma correcta de actuar sería:


char cadena[10];
cadena[ 0 ] = 'A';
cadena[ 1 ] = '\0';
printf( "%s", cadena );


Ahora, el 100% de las ejecuciones van a sacar una única A por pantalla.

m@o_614

si ya entendi que cada vez que me encuentre una coma tengo que agregarle el final de linea, y tambien le cambie otras cosas al codigo como que tambien le quite el salto de linea en el fgets y cada vez que compare el nombre con el otro nombre que tengo almacenado en cadena limpio el arreglo con memset(), para que cada vez despues de la coma ingrese un nuevo nombre a un buffer limpio pero aun asi no funciona

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM 10

int main()
{
    FILE *fd;
    int i,j,tam;
    char nombre[TAM],*cadena,car,*ptr;
    if((fd = fopen("F:\\nombre_archivo.txt","r"))!= NULL)
    {
        printf("Capture nombre a buscar: ");
        fgets(nombre,TAM,stdin);
        if((ptr = strchr(nombre,'\n')) != NULL)
           *ptr = '\0';
        tam = strlen(nombre);
        cadena  = (char*)malloc((tam+1)*sizeof(char));
        i = 0;
        while(!feof(fd))
        {
            j = 0;
            while((car = fgetc(fd))!= ',')
            {
                cadena[j] = car;
                j++;
            }
            cadena[j] = '\0';
            if((strcmp(cadena,nombre)) == 0)
               i++;
            memset(cadena,0,tam+1);
        }
        printf("El nombre %s se encuentra %d veces en el archivo\n",nombre,i);
    }
    else
       printf("No se pudo abrir archivo");
    return 0;
}


gracias

eferion

Tienes varios fallos:

* Cadena no puede tener la longitud del nombre buscado más uno. Te pongo un ejemplo, si buscas ANGEL y el nombre que te toca leer es ROBERTO... pues ROBERTO no va a entrar en 6 caracteres, necesita 8. Lo ideal es que cadena tenga una longitud lo suficentemente larga como para aceptar cualquier nombre. 100 o 200 caracteres suelen ser suficientes.

* El memset no te sirve para nada, ya terminas la cadena con el '\0', luego cadena siempre va a contener un string válido.

* La variable tam puedes eliminarla sin problemas, no la necesitas para nada.

* TAM está definida a 10, esto te limita el nombre a buscar a 9 caracteres... igual que con el primer punto, te puedes permitir el lujo de ampliarlo un poco.

* La línea del strchr no tiene mucha lógica... cuando tu lees con fgets ya te está finalizando la cadena con '\0'

Y así a bote pronto no veo nada más. Corrige eso a ver si te funciona mejor.

Un saludo.

ecfisa

#6
Hola.

Otro problema que veo, es que estas presuponiendo que "cadena" va a tener el mismo largo que "nombre" ya que con malloc le reservas la misma cantidad de memoria.

Me explico, si por ejemplo ingresas "Ana" y en el archivo existe el nombre "Hermenegildo" cuando leas ese nombre, la variable "cadena" no va a tener el  espacio necesario para almacenarlo...

Proba de este modo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM 50

int main()
{
   FILE *fd;
   int veces = 0, i;
   char captura[TAM], *cadena, *nombre, car;

   if((fd = fopen("F:\\nombre_archivo.txt", "r"))== NULL) {
     printf("Error abriendo archivo");
     return EXIT_FAILURE;
   }

   printf("Ingrese nombre a buscar: ");
   fgets(captura, TAM, stdin);
   captura[strlen(captura)-1] = '\0';

   nombre = (char*)malloc(strlen(captura)*sizeof(char));
   cadena = (char*)malloc(sizeof(char));
   strcpy(nombre, captura);

   while(!feof(fd)) {
     i = 0;
     while(!feof(fd) && (car = fgetc(fd)) != ','  ) {
       cadena = (char*)realloc(cadena,(i+1)*sizeof(char));
       cadena[i++] = car;
     }
     cadena[i] = '\0';
     if(strcmp(cadena, nombre) == 0) veces++;
   }

   printf("El nombre %s ");
   switch(veces) {
     case 0: printf("no se encuentra en el archivo.\n");break;
     case 1: printf("se encuentra 1 vez en el archivo.\n");break;
     default:
       printf("se encuentra %d veces en el archivo.\n",veces);
   }

   free(cadena);
   free(nombre);

   return EXIT_SUCCESS;
}


El ejemplo no contempla que el archivo finalize sin una coma. Si no la tiene y buscas un nombre que esté al final, contabilizará una ocurrencia menos o no lo encontrará de ser el único.

Saludos :)

Edito: Veo que mientras escribía eferion ya señalizó esta falla.

m@o_614

Ahora el programa ya me hace lo que le pido, solo que tengo que corregir esa ultima falla de si el nombre buscado se encuentra al final que tambien lo contabilice, ecfisa una ultima duda, por que en tu programa pusiste la funcion feof() en dos whiles?? no es suficiente con uno??

 while(!feof(fd)) {
     i = 0;
     while(!feof(fd) && (car = fgetc(fd)) != ','  ) {
       cadena = (char*)realloc(cadena,(i+1)*sizeof(char));
       cadena[i++] = car;
     }
     cadena[i] = '\0';
     if(strcmp(cadena, nombre) == 0) veces++;
   }


por que cambie el codigo un poco y con este detalle me funciona pero si le quito un feof ya no funciona, le tengo que poner 2:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM 20

int main()
{
   FILE *fd;
   int i = 0,j;
   char nombre[TAM],cadena[TAM],car,*ptr;
   if((fd = fopen("F:\\nombre_archivo.txt","r"))!= NULL)
   {
       printf("Capture nombre a buscar: ");
       fgets(nombre,TAM,stdin);
       if((ptr = strchr(nombre,'\n')) != NULL)
          *ptr = '\0';
       while(!feof(fd))
       {
           j = 0;
           while((car = fgetc(fd))!= ','&& !feof(fd))
               cadena[j++] = car;
           cadena[j] = '\0';
           if((strcmp(cadena,nombre)) == 0)
              i++;
       }
       printf("El nombre %s aparece %d veces en el archivo\n",nombre,i);
   }
   else
      printf("No se pudo abrir archivo");
   return 0;
}


muchas gracias a ambos por sus respuestas

saludos

ecfisa

Cita de: m@o_614 en  2 Octubre 2013, 23:55 PM
ecfisa una ultima duda, por que en tu programa pusiste la funcion feof() en dos whiles?? 
Hola.

Por que de ese modo el ciclo interior puede detectar el fin del archivo, saber que no existen mas caracteres para leer y por tanto finalizar.

Tal vez te resulte mas claro si se expande un poco el código:

...
   // mientras no fin de archivo
   while(!feof(fd)) { 
      i = 0;
      // leer 1 caracter
      car = fgetc(fd);
     // mientras no fin de archivo y el caracter es distinto de ','
      while(!feof(fd) && car != ',')
        // almacenar en variable "cadena"
        cadena = (char*)realloc(cadena,(i+1)*sizeof(char));
        cadena[i++] = car; 
        // leer proximo caracter
        car = fgetc(fd);             
      } // ( se lleyo fin de archivo o una coma )
      cadena[i] = '\0';
      if(strcmp(cadena, nombre) == 0) veces++;
    } //( si "car" es coma continua, si es EOF finaliza )
...


Saludos :)

rir3760

Cita de: m@o_614 en  2 Octubre 2013, 23:55 PMAhora el programa ya me hace lo que le pido, solo que tengo que corregir esa ultima falla de si el nombre buscado se encuentra al final que tambien lo contabilice
Ademas de las recomendaciones que ya te dieron hay que cambiar el tipo de la variable "car" ya que el valor de retorno de fgetc es int, hay que cerrar el archivo con fclose y se puede eliminar el uso de feof si, en su lugar, se verifica el valor de retorno de fgetc:
j = 0;
do {
   if ((car = fgetc(fd)) == ',' || car == EOF){
      cadena[j] = '\0';
      if ((strcmp(cadena, nombre)) == 0)
         i++;
      j = 0;
   }else
      cadena[j++] = car;
}while (car != EOF);
fclose(fd);


Algo que no mencionas es si el archivo consiste de una sola linea o varias, en el segundo caso se debe considerar al avance de linea como otro separador. Una opciona aqui es utilizar la funcion fscanf para obtener cada uno de los nombres:
while (fscanf(fd, "%[^,\n]%*c", cadena) == 1)
   if ((strcmp(cadena, nombre)) == 0)
      i++;
fclose(fd);


Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language