Lettura stringa con fread dopo malloc

di il
25 risposte

25 Risposte - Pagina 2

  • Re: Lettura stringa con fread dopo malloc

    oregon ha scritto:


    A me non succede. Elimina il file dei dati e riprova.

    Se hai altri problemi allora hai cambiato qualche altra parte del programma. Quindi mostralo nuovamente tutto. Mi raccomando, mostralo tutto anche se credi che sia uguale altrimenti perdiamo solo tempo.
    Ah allora si, condivido il codice completo. Provando più casi limite ho notato che non è detto che tronchi al primo spazio. Spesso succede anche con il secondo o con altri in modo "apparentemente casuale" (anche se 100% è qualche mio errore stupido).
    Grazie davvero:
    void input() {
         char *str;
         int n=10,l;
         char ask[5];
         FILE *in;
         
         in = fopen("file.dat","wb");
         if(!in) {
             perror("Errore di apertura");
             exit(1);
           }
         
         while(n<809 && strcmp(ask,"si")!=0) {
             printf("\nBastano %d caratteri per la tua frase? [si] [no]",n);
             scanf("%s",ask);
             if(strcmp(ask,"no")==0)
                 n+=50;
          }
    
         str = (char *) malloc(n*sizeof(char));
         
         fflush(stdin);
         
         printf("\nInserisci la frase [max caratteri: 807]");
         fgets(str,strlen(str),stdin);
         
         fflush(stdin);
         
         l = strlen(str);
         fwrite(&l,sizeof(l),1,in);
         fwrite(str,strlen(str),1,in);
         
         printf("\nLa tua frase e' stata inserita correttamente");
         
         fclose(in);
         free(str);
    }
    
    void output() {
         int lun;
         char *buf;
         FILE *out;
         
         printf("Prima di premere INVIO, assicurati che:");
         printf("\nil file sia presente nella cartella del programma");
         printf("\n\n");
         system("PAUSE");
         
         out = fopen("file.dat","rb");
         if(!out) {
             perror("Errore di apertura");
             exit(1);
           }
         
         fread(&lun,sizeof(lun),1,out);
          
         buf = (char *) malloc((lun+1)*sizeof(char));
         
         fread(buf,lun*sizeof(char),1,out);
         
         buf[lun*sizeof(char)] = '\0';
         
         printf("\n%s",buf);
         
         free(buf);
         
         fclose(out);
    
         printf("\n\nSi consiglia di eliminare il file \n"); 
    }
  • Re: Lettura stringa con fread dopo malloc

    Elimina i fflush(stdin) e aggiungi una variabile di comodo

    char du;

    poi modifica la scanf in questo modo

    scanf("%s%c", ask, &du);

    per eliminare il carattere \n dal buffer.
  • Re: Lettura stringa con fread dopo malloc

    oregon ha scritto:


    Elimina i fflush(stdin) e aggiungi una variabile di comodo

    char du;

    poi modifica la scanf in questo modo

    scanf("%s%c", ask, &du);

    per eliminare il carattere \n dal buffer.
    Wow, funziona tutto. In pratica così hai forzato l'inserimento di \n in un altra variabile.
    Direi che ora posso procedere senza altri aiuti, terrò bene a conto di tutti i consigli che mi hai dato.
    Grazie per il tuo tempo e buona serata!
    Lorenzo
  • Re: Lettura stringa con fread dopo malloc

    oregon ha scritto:


    Elimina i fflush(stdin) e aggiungi una variabile di comodo

    char du;

    poi modifica la scanf in questo modo

    scanf("%s%c", ask, &du);

    per eliminare il carattere \n dal buffer.
    Eh no, purtroppo permane un problema
    Ora non alloca più di circa 90 char. E se provo a togliere l'inserimento, e a sostituire n a un numero superiore a quel limite, il programma crasha
    (Ho cambiato solo quelle cose, nient'altro)
  • Re: Lettura stringa con fread dopo malloc

    Cioè ? Che fai?
  • Re: Lettura stringa con fread dopo malloc

    oregon ha scritto:


    Cioè ? Che fai?
    Ad esempio, eseguendo questa funzione non mi viene proprio chiesto nulla. Immaginavo fosse malloc che non alloca, ma facendo il test con NULL non sembra essere quello...
    void input() {
         char *str;
         int n=10,l,j;
         char ask[5],app;
         FILE *in;
         
         in = fopen("_3n!gmA_.dat","wb");
         if(!in) {
             perror("Errore di apertura");
             exit(1);
           }
         
         //HO TOLTO QUESTA  PARTE PER FACILITARE IL TEST, TAGLIANDO LA PARTE DI ASSEGNAZIONE DI N DELL'UTENTE
    
        /* while(n<100 && strcmp(ask,"si")!=0) {
             printf("\nBastano %d caratteri per la tua frase? [si] [no]",n);
             scanf("%s%c",ask,&app);
             if(strcmp(ask,"no")==0)
                 n+=40;
          }*/
         
         str = (char *) malloc(400*sizeof(char)); //MA ANCHE CON 150/200/600/qualsiasi sopra una certa soglia
         
         //TUTTO IL PROSSIMO CODICE VIENE "IGNORATO"
         
         printf("\nInserisci la frase [max caratteri: 100]");
         fgets(str,strlen(str),stdin);
         
         l = strlen(str);
         
         fwrite(&l,sizeof(l),1,in);
         fwrite(str,strlen(str),1,in);
         
         printf("\nLa tua frase e' stata inserita correttamente");
         printf("\nIl file cryptato e' nella cartella del programma");
         
         fclose(in);
         free(str);
    }
  • Re: Lettura stringa con fread dopo malloc

    Non capisco il problema. A me funziona.

    Ovviamente ho cambiato il nome del file sia in input che in output.
  • Re: Lettura stringa con fread dopo malloc

    lore_majo ha scritto:


    str = (char *) malloc (n*sizeof(char));
    scanf("%s", str); --> fgets(str,strlen(str),stdin);
    @lore_majo, in questo codice c'e' un errore MOOLTO subdolo.

    Per prima cosa, e' buona norma (non obbligatorio, ma aiuta) usare come nome delle costanti scritte tutte in maiuscolo, e magari anche usare un nome significativo che aiuta a capire.

    Ad esempio, invece di "n", "MAX_STR_LEN".

    Seconda cosa: invece di usare la "malloc", usare la "calloc" (leggiti la documentazioni: le due funzioni NON HANNO lo stesso numero di parametri).

    La differenza tra le due funzioni e' significativa: la "calloc" INIZIALIZZA il blocco di memoria allocato, con ZERO.
    Non preoccuparti dei tempi: si parla di nanosecondi per blocchi di memoria di gigabyte: ci sono istruzioni assembler dedicate.

    Ed ora vediamo l'errore:
    fgets(str,strlen(str),stdin)
    Qui' tu usi la "strlen", ma la strlen ritorna la lunghezza della stringa memorizzata in "str", NON la dimensione del buffer allocato.

    Purtroppo in C/C++ non esiste una API standard che ti permetta di sapere la dimensione del buffer, ne devi tenere traccia tu in qualche modo.

    Ora, hai tre tipi di rogne:

    1) se "str" e' stato inizializzato con zero, "strlen(str)" ritorna ZERO, quindi stai dicendo alla fgets che il tuo buffer e' lungo ZERO: cioe' non hai spazio nemmeno per UN carattere

    2) se "str" NON E' stato inizializzato e sfortuna vuole che non contenza NESSUN ZERO, poiche' la strlen e' stupida e cerca il primo zero che trova, rischi di passare una lunghezza che e' MAGGIORE DELLA DIMENSIONE DEL BUFFER, con il risultato che potenzialmente potresti andare a scrivere in aree di memoria riservate.

    3) il CASO PEGGIORE, "str" contiene caratteri a caso ed anche uno zero in qualche posizione.

    Questo vuol dire che strlen ritorna una lunghezza a caso compresa tra 0 e MAX_STR_LEN. Se hai cu.. /fortuna, c'e' abbastanza spazio per leggere la stringa in ingresso, altrimenti leggi solo un po' di caratteri e ti perdi il resto.

    Il problema sta' nel fatto che questo pezzetto di codice OGNI TANTO FUNZIONA, OGNI TANTO NO. E non hai modo di prevederlo.

    Quello che si chiama: "FUNZIONA PER SBAGLIO".

    Ci sono una serie di nuove funzioni, con sufisso "_s" (per "safe") in cui si DEVE passare la lunghezza del buffer e che aiutano ad evitare questo tipo di pasticci.

    AIUTANO, NON RISOLVONO.
  • Re: Lettura stringa con fread dopo malloc

    migliorabile ha scritto:


    Seconda cosa: invece di usare la "malloc", usare la "calloc" (leggiti la documentazioni: le due funzioni NON HANNO lo stesso numero di parametri).

    La differenza tra le due funzioni e' significativa: la "calloc" INIZIALIZZA il blocco di memoria allocato, con ZERO.
    Non preoccuparti dei tempi: si parla di nanosecondi per blocchi di memoria di gigabyte: ci sono istruzioni assembler dedicate.
    Perché si deve usare calloc al posto di malloc se non si ha bisogno che tutto il buffer sia inizializzato a zero?
  • Re: Lettura stringa con fread dopo malloc

    Im effetti mi era sfuggita in

    fgets(str,strlen(str),stdin);

    non usare la strlen ma il massimo numero di caratteri allocati nel buffer.
  • Re: Lettura stringa con fread dopo malloc

    migliorabile ha scritto:


    lore_majo ha scritto:


    str = (char *) malloc (n*sizeof(char));
    scanf("%s", str); --> fgets(str,strlen(str),stdin);
    @lore_majo, in questo codice c'e' un errore MOOLTO subdolo.

    Per prima cosa, e' buona norma (non obbligatorio, ma aiuta) usare come nome delle costanti scritte tutte in maiuscolo, e magari anche usare un nome significativo che aiuta a capire.

    Ad esempio, invece di "n", "MAX_STR_LEN".

    Seconda cosa: invece di usare la "malloc", usare la "calloc" (leggiti la documentazioni: le due funzioni NON HANNO lo stesso numero di parametri).

    La differenza tra le due funzioni e' significativa: la "calloc" INIZIALIZZA il blocco di memoria allocato, con ZERO.
    Non preoccuparti dei tempi: si parla di nanosecondi per blocchi di memoria di gigabyte: ci sono istruzioni assembler dedicate.

    Ed ora vediamo l'errore:
    fgets(str,strlen(str),stdin)
    Qui' tu usi la "strlen", ma la strlen ritorna la lunghezza della stringa memorizzata in "str", NON la dimensione del buffer allocato.

    Purtroppo in C/C++ non esiste una API standard che ti permetta di sapere la dimensione del buffer, ne devi tenere traccia tu in qualche modo.

    Ora, hai tre tipi di rogne:

    1) se "str" e' stato inizializzato con zero, "strlen(str)" ritorna ZERO, quindi stai dicendo alla fgets che il tuo buffer e' lungo ZERO: cioe' non hai spazio nemmeno per UN carattere

    2) se "str" NON E' stato inizializzato e sfortuna vuole che non contenza NESSUN ZERO, poiche' la strlen e' stupida e cerca il primo zero che trova, rischi di passare una lunghezza che e' MAGGIORE DELLA DIMENSIONE DEL BUFFER, con il risultato che potenzialmente potresti andare a scrivere in aree di memoria riservate.

    3) il CASO PEGGIORE, "str" contiene caratteri a caso ed anche uno zero in qualche posizione.

    Questo vuol dire che strlen ritorna una lunghezza a caso compresa tra 0 e MAX_STR_LEN. Se hai cu.. /fortuna, c'e' abbastanza spazio per leggere la stringa in ingresso, altrimenti leggi solo un po' di caratteri e ti perdi il resto.

    Il problema sta' nel fatto che questo pezzetto di codice OGNI TANTO FUNZIONA, OGNI TANTO NO. E non hai modo di prevederlo.

    Quello che si chiama: "FUNZIONA PER SBAGLIO".

    Ci sono una serie di nuove funzioni, con sufisso "_s" (per "safe") in cui si DEVE passare la lunghezza del buffer e che aiutano ad evitare questo tipo di pasticci.

    AIUTANO, NON RISOLVONO.
    Okay, smanettando per un pò e ragionando per bene su quanto mi hai detto ne sono venuto a capo.
    Ora il programma funziona con spazi e dimensioni volute annesse.
    Ringrazio infinitamente te per questa spiegazione e @oregon per tutti i preziosi aiuti.
    Direi che ho imparato varie cose importanti riguardo stringhe e allocazioni dinamiche che prima non sapevo. Purtroppo calloc non la avevo ancora affrontata accademicamente fino a giusto giusto oggi, quindi ho avuto anche io l'illuminazione.
    Ringrazio ancora tutti!
Devi accedere o registrarti per scrivere nel forum
25 risposte