Problema leyendo cadenas con memoria dinamica [solucionado]

Iniciado por dato000, 20 Enero 2014, 23:46 PM

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

dato000

Buenas gente, necesito una mano, ayudenme con este problema

Poco a poco avanzo con memoria dinamica, me gusta más usar calloc  :xD :xD

ahora mi problema es que en el problema me estan pidiendo un arreglo multidimensional, tratando de leer multiples cadenas dependiendo de cuantas cadenas quiere el usuario, y ahi es donde me quedo corto a la hora de crear la reserva de la memoria.

No creo que sea tan dificil, pero realmente los ejercicios que estoy consultando estan un poco enredados y más que ayudarme, me confunden mucho más porque estan mezclando elementos de cadenas con estruturas y cosas así, necesito una aclaración de uds compañeros.


/*
   Escribir un programa para leer n cadenas de caracteres. Cada cadena tiene una longitud variable
   y esta formada por cualquier carácter. La memoria que ocupa cada cadena se ha de ajustar al
   tamaño que tiene. Una vez leidas las cadenas se debe realizar un proceso que consiste en eliminar
   todos los blancos, siempre manteniendo el espacio ocupado ajustado al número de caracteres.

   El programa debe mostrar las cadenas leídas y las cadenas transformadas.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define longitud 100

int main()
{
   char *cadena;
   int contador=0;
   int i;
   cadena = (char*) calloc(longitud, sizeof(char));

   if(cadena == NULL)
   {
       printf("\n\t  Error de asignación de memoria, adios!!");
       exit(-1);
   }

   printf("Digite la cadena:  ");
   fgets(cadena, longitud, stdin);



   for(i=0; i<strlen(cadena); i++)
   {
       if(cadena[i] == ' ')
       {
           cadena[i] = cadena[i+1];
           contador++;
       }
   }

   printf("\n\n\t La cadena es: %s", cadena);
   printf("\t Cantidad de espacios:  %d", contador);
   printf("\n\t longitud: %d  -- strlen(cadena):  %d", longitud, strlen(cadena));

   free(cadena);
   return 0;
}


Les agradezco cualquier pista

slds



rir3760

Debes reservar un bloque para cada una de las cadenas, con esto no debes tener problemas. Ademas la dirección donde se almacena cada una ellas la debes almacenar en otro bloque.

Un ejemplo básico sin reajuste ni validaciones:
#include <stdio.h>
#include <stdlib.h>

#define MAX_CHARS  4096

int main(void)
{
   char **linea;
   int num_lineas;
   int ch;
   int i;
   
   printf("Numero de lineas: ");
   fflush(stdout);
   scanf("%d", &num_lineas);
   while ((ch = getchar()) != EOF && ch != '\n')
      ;
   
   /* Reserva del bloque de memoria principal */
   linea = malloc(num_lineas * sizeof *linea);
   
   /* Lectura de cada una de las lineas */
   for (i = 0; i < num_lineas; i++){
      /* Reserva del bloque de memoria inicial para cada linea */
      linea[i] = malloc(MAX_CHARS);
     
      printf("Introduce la linea no %d:\n", i + 1);
      fgets(linea[i], MAX_CHARS, stdin);
   }
   
   /* Impresion de cada una de las lineas */
   for (i = 0; i < num_lineas; i++)
      printf("%2d: %s", i + 1, linea[i]);
   
   /* Primero se libera la memoria de cada linea ... */
   for (i = 0; i < num_lineas; i++)
      free(linea[i]);
   /* ... a continuacion el bloque de memoria principal */
   free(linea);
   
   return EXIT_SUCCESS;
}

Se deben agregar las validaciones de las funciones de reserva de memoria así como E/S y también se debe agregar la lógica necesaria en el caso de una linea que rebase el limite (reajuste mediante realloc).

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

do-while

¡Buenas!

Lo que no debes hacer es asignar una cantidad de memoria predeterminada a una cadena, lo hagas asignando esa memoria de forma dinamica o estatica, ya que si defines, por ejemplo, una longitud de 1000 caracteres puedes estar desaprobechando memoria si se introduce una cadena de menor longitud o puedes estar perdiendo informacion si tiene mayor longitud.

Lo que tienes que hacer es leer caracteres con una cadena de un tamaño determinado (por ejemplo 10 caracteres) y mediante realloc añadirlos a la linea que introduzcas. ¿Como sabras cuando has terminado de leer una linea? utiliza fgets, deja de leer cuando detecta un '\n' y lo almacena en la cadena:


hacer
{
    leer_cadena(lector);

    si(reasignar_memoria(linea, longitud(linea) + longitud(lector) + 1)
    {
        concatenar(linea,lector);
    }
}
mientras(lector[longitud(lector) - 1] != '\n');

linea[longitud(linea) - 1] = '\0';


Creo que es la unica manera que tienes de leer el contenido de una linea sin saber de antemano cual es la longitud maxima que puede tener.

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

dato000

Muchas gracias por la ayuda, me sirvio bastante las pistas, pues aún no se como implementar bien lo de realloc, pero ya voy entendiendo como trabaja esto, finalmente pude sacarlo como queria, dejo el código, pero pues, como podría adaptarse la parte realloc sin tener un máximo constante para el número de caracteres que debe reservarse en memoria sin que se desperdicie??


/*
    Escribir un programa para leer n cadenas de caracteres. Cada cadena tiene una longitud variable
    y esta formada por cualquier carácter. La memoria que ocupa cada cadena se ha de ajustar al
    tamaño que tiene. Una vez leidas las cadenas se debe realizar un proceso que consiste en eliminar
    todos los blancos, siempre manteniendo el espacio ocupado ajustado al número de caracteres.

    El programa debe mostrar las cadenas leídas y las cadenas transformadas.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define longitud 100

int main()
{
    char **cadena;
    int num_cadenas;
    int contador=0;
    int i,j;

    do
    {
        printf("Digite el numero de cadenas:  ");
        scanf("%d%c", &num_cadenas);
    }while(num_cadenas < 1);

    cadena = (char**) calloc(num_cadenas, sizeof(char*));

    if(cadena == NULL)
    {
        printf("\n\t  Error de asignación de memoria, adios!!");
        exit(-1);
    }

    // leyendo cada linea en memoria
    for(i=0; i<num_cadenas; i++)
    {
        // Reservando el bloque de memoria para cada linea
        cadena[i] = (char*) calloc(longitud, sizeof(char)); // +1 por '\n'
        printf("Digite la cadena # %d:  ", i+1);
        fgets(cadena[i], longitud, stdin);


    }

    for(i=0; i<num_cadenas; i++)
    {
        for(j=0; j<strlen(cadena[i]); j++ )
        {
            if(cadena[i][j] == ' ')
            {
                cadena[i][j] = cadena[i][j+1];
            }
        }
    }


    for (i = 0; i < num_cadenas; i++)
        printf("cadena # %2d: %s", i + 1, cadena[i]);


    /* Primero se libera la memoria de cada linea ... */
    for (i = 0; i < num_cadenas; i++)
        free(cadena[i]);

    /* ... a continuacion el bloque de memoria principal */
    free(cadena);
    return 0;
}



amchacon

Ten en cuenta que hoy en día importa más la eficiencia que la memoria, hoy en día nadie se preocupa por 6-7 bytes de más pero sí un progama tarda menos o no. El realloc es una operación costosa (creas un nuevo vector, copias el contenido al nuevo vector byte a byte y borras el antiguo).

No obstante, te presento una versión con realloc "amortiguado":
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void LeerLinea(char** Linea)
{
   const int Incremento = 5;
   int tam = 0;
   int max = Incremento;
   int ch;

   (*Linea) = (char*) calloc(max,sizeof(char));

   while((ch = getchar()) != '\n' && ch != EOF)
   {
       (*Linea)[tam++] = ch;

       if (tam == max)
       {
           max += Incremento;
           (*Linea) = (char*)realloc (*Linea,max);

           if (*Linea == NULL)
           {
                printf("\n\t  Error de asignación de memoria, adios!!");
                exit(-1);
           }
       }
   }

   (*Linea)[tam] = 0; // caracter nulo
}

int main()
{
   char **cadena;
   int num_cadenas;
   int contador=0;
   int i,j;

   do
   {
       printf("Digite el numero de cadenas:  ");
       scanf("%d", &num_cadenas);
   }
   while(num_cadenas < 1);

   getchar();

   cadena = (char**) calloc(num_cadenas, sizeof(char*));

   if(cadena == NULL)
   {
       printf("\n\t  Error de asignación de memoria, adios!!");
       exit(-1);
   }

   // leyendo cada linea en memoria
   for(i=0; i<num_cadenas; i++)
   {
       LeerLinea(&cadena[i]);
   }

   for(i=0; i<num_cadenas; i++)
   {
       for(j=0;cadena[i]; j++ )
       {
           if(cadena[i][j] == ' ')
           {
               cadena[i][j] = cadena[i][j+1];
           }
       }
   }


   for (i = 0; i < num_cadenas; i++)
       printf("cadena # %2d: %s\n", i + 1, cadena[i]);


   /* Primero se libera la memoria de cada linea ... */
   for (i = 0; i < num_cadenas; i++)
       free(cadena[i]);

   /* ... a continuacion el bloque de memoria principal */
   free(cadena);
   return 0;
}


Es "realloc amortiguado" porque aumento la memoria de 5 en 5 en vez de 1 en 1. Así hago mucho menos reallocs y como mucho desperdicio 4 bytes.
Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar

dato000

Cierto es, pero a modo de laboratorio me ayudo bastante, muchas gracias.



rir3760

Un comentario: en este momento los caracteres ' ' no se eliminan correctamente (solo se sobrescriben con el siguiente carácter). Para eliminarlos de la cadena hay que utilizar un bucle mas o menos así:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
   char a[] = "Esta es alguna cadena con espacios";
   int i;
   int j;
   
   printf("\"%s\"\n", a);
   
   i = j = 0;
   do {
      if (a[i] != ' ')
         a[j++] = a[i];
   }while (a[i++] != '\0');
   
   /*
   ** En tu programa el bloque debe reajustarse, el
   ** numero de bytes esta dado por la variable "j"
   */
   
   printf("\"%s\"\n", a);
   
   return EXIT_SUCCESS;
}


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

amchacon

Cita de: rir3760 en 24 Enero 2014, 17:55 PM
Un comentario: en este momento los caracteres ' ' no se eliminan correctamente (solo se sobrescriben con el siguiente carácter). Para eliminarlos de la cadena hay que utilizar un bucle mas o menos así
Te refieres a que sobran caracteres al final no?

Serviría el mismo código, solo habría que hacer un realloc con el nuevo tamaño de la cadena y listo.
Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar

rir3760

Cita de: amchacon en 24 Enero 2014, 18:04 PMTe refieres a que sobran caracteres al final no?
En parte ya que otro problema se presenta si la cadena es "a  b  c" (dos espacios como separacion) la cadena resultante sera "a bb cc".

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

dato000

Cierto, se sobreescriben al final, pero ya seria el manejo del indice del arreglo, a modo de laboratorio podrian dejarse los espacios sobrantes como un valor cero o '\0' o usar un realloc para eso, pero pues, el objetivo esta cumplido, yo sigo con otro ejercicio.