PDA

View Full Version : [C] Copiare file in una stringa


xbubbax
12-03-2008, 15:42
Ho fatto una semplice funzione per copiare il testo di un file in una stringa ma mi da errore quando lo eseguo, esce il solito errore di windows, "non inviare"... come mai?

#include <stdio.h>
#include <stdlib.h>

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

char c=0;
static char *s;

while(c!=EOF){

c=fgetc(in);
*s=c;
*s=*s+1;}

return s;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

*s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

71104
12-03-2008, 15:47
perché all'interno di readFile non hai allocato s

xbubbax
12-03-2008, 15:54
in che senso? non capisco...

andbin
12-03-2008, 16:10
in che senso? non capisco...'s' è una variabile static (vuol solo dire che è a durata "fissa", non è allocata sullo stack). Ma è un puntatore non inizializzato .... non si sa a cosa punta.
Devi prima fare in modo che 's' punti ad una area di memoria di dimensione sufficiente per quello che devi fare.

Anche la chiamata non va bene:
*s=readFile(f1);

L'asterisco non ci vuole.

E guardando meglio la funzione:
*s=*s+1;

non è tecnicamente sbagliato ma non ha molto senso (non so cosa volevi fare ... incrementare il puntatore?)

xbubbax
12-03-2008, 16:15
da quanto tempo andbin

allora ho tolto l'asterisco dove non andava bene...

con *s=*s+1 voglio incrementare la stringa, cioè passare alla locazione successiva dove memorizzare un carattere preso dal file. è sbagliato?

non capisco ancora come allocare s all'interno della funzione. devo usare malloc? se si come?

andbin
12-03-2008, 16:50
con *s=*s+1 voglio incrementare la stringa, cioè passare alla locazione successiva dove memorizzare un carattere preso dal file. è sbagliato?No non va bene. L'operatore di dereferenziazione * ha priorità maggiore del +, quindi prima prenderebbe il carattere puntato da s (es. 'a'), poi lo incrementa di 1 (diventa 'b') e poi lo assegna di nuovo nel carattere puntato da s.

Se vuoi incrementare il puntatore basta che fai s++;

In ogni caso leggere 1 byte per volta è inefficiente e scomodo. Meglio usare fread() per leggere un blocco di byte.

non capisco ancora come allocare s all'interno della funzione. devo usare malloc? se si come?Sì con malloc. Se non conosci a priori la dimensione del file, ci sono diversi scenari:
a) Aprire il file, fare una fseek() al fondo e determinare la lunghezza con ftell(), quindi fare una fseek() all'inizio. In tal caso si alloca un unico blocco di memoria che può già contenere l'intero file.
b) Allocare inizialmente un tot di memoria che poi viene riallocata man mano che serve facendo un ciclo di N letture.
c) Allocare un tot di memoria sufficientemente grande e leggere al massimo per quella dimensione, non di più.

xbubbax
12-03-2008, 16:53
io ho sempre usato malloc solo con le liste, non ho idea di cosa inserire ora, cioè di come allocare memoria.

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

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

char c=0;
static char *s=( )malloc(sizeof( ));

while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return s;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

andbin
12-03-2008, 17:41
non ho idea di cosa inserire ora, cioè di come allocare memoria.Ma è appunto lì la questione, ho anche spiegato le varie possibilità. Dipende tutto da come vuoi gestire il (ri)dimensionamento dell'area di memoria.

xbubbax
12-03-2008, 18:06
sisi ho capito che vuoi dire, il problema mio è proprio scrivere le righe di codice, ovvero la sintassi. non ho mai usato quelle funzioni che hai nominato

xbubbax
13-03-2008, 09:56
andbin dei modi che mi hai elencato per allocare memoria quale mi conviene usare?

andbin
13-03-2008, 10:57
andbin dei modi che mi hai elencato per allocare memoria quale mi conviene usare?Io direi quella che ho indicato con a). Determini prima la dimensione del file, allochi memoria esattamente per quella dimensione e quindi carichi tutto il file.

xbubbax
13-03-2008, 11:25
Vada per la a. Sto cercando le funzioni che mi servono ma non ho idea di come usarle.

int fseek(FILE *stream, long offset, int whence);

long ftell(FILE *stream);

non so come usarle, non l'ìho mai fatto, mi puoi fare un esempio simile al mio...

andbin
13-03-2008, 12:04
Ecco un esempio che avevo già scritto in passato:

long get_file_size (FILE *stream)
{
long pos, size;

if ((pos = ftell (stream)) == -1)
return -1; /* errore */

if (fseek (stream, 0, SEEK_END) != 0)
return -1; /* errore */

if ((size = ftell (stream)) == -1)
return -1; /* errore */

if (fseek (stream, pos, SEEK_SET) != 0)
return -1; /* errore */

return size;
}È molto "generica" perché si preoccupa di salvare/ripristinare la posizione corrente, qualunque essa sia.
Se non ti è chiaro, chiedi pure.

xbubbax
13-03-2008, 13:11
quindi se ho capito bene devo fare una funzione apposta per calcolare la lunghezza del file o posso includerla nella mia funzione?

una volta che ho fatto questo cosa devo assegnare al puntatore static char*s?

size? o cosa

andbin
13-03-2008, 13:42
quindi se ho capito bene devo fare una funzione apposta per calcolare la lunghezza del file o posso includerla nella mia funzione?Se vuoi usare la "mia" funzione così come è, basta che la metti ad esempio all'inizio del tuo sorgente.
L'ho postata perché capissi il concetto. Non sei obbligato ad usare la mia funzione, puoi anche solo chiamare le singole fseek/ftell dove ti servono. Basta che fai le 3 fasi indispensabili: a) sposti al fondo, b) leggi la lunghezza, c) sposti all'inizio.

una volta che ho fatto questo cosa devo assegnare al puntatore static char*s?La lunghezza ottenuta la passi a malloc, che ti dà un puntatore all'area allocata che poi assegni alla tua variabile puntatore.

xbubbax
13-03-2008, 14:04
in teoria dovrebbe venire una cosa del genere?

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

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

long pos, size;
char c=0;
static char *s;

if ((pos = ftell (in)) == -1)
return -1; /* errore */

if (fseek (stream, 0, SEEK_END) != 0)
return -1; /* errore */

if ((size = ftell (in)) == -1)
return -1; /* errore */

if (fseek (stream, pos, SEEK_SET) != 0)
return -1; /* errore */

s=malloc(sizeof(size));



while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return s;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

però mi da errore sui vari return -1 dei tuoi if, penso perchè la mia funzione non deve ritornare un intero.

andbin
13-03-2008, 14:30
in teoria dovrebbe venire una cosa del genere?Concettualmente sì, salvo gli errori che tra poco ti riporto.


if ((pos = ftell (in)) == -1)
return -1; /* errore */La mia funzione ritorna un long, quindi per indicare errore ho fatto ritornare -1, che sicuramente non è una lunghezza valida.

Nella tua funzione non puoi far ritornare -1! Infatti restituisce un char*. Presta meglio attenzione alla gestione degli errori. Piuttosto fai ritornare NULL in caso di errore (e nel main dovrai ovviamente anche testarlo).

s=malloc(sizeof(size));sizeof fornisce la dimensione in byte di una variabile o un tipo, quindi non va bene. Visto che size è un long, sizeof(size) ritorna sempre 4 su un sistema a 32 bit!!

s=malloc(size);

while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}Come avevo già detto, leggere un byte per volta è scomdo ed inefficiente. Usa fread().

xbubbax
13-03-2008, 14:46
ho corretto tutto ma mi da ancora errore.

per la fread basta sostituirla alla fgetc oppure devo includere qualche libreria?

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

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

long pos, size;
char c=0;
static char *s;

if ((pos = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, 0, SEEK_END) != 0)
return NULL; /* errore */

if ((size = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, pos, SEEK_SET) != 0)
return NULL; /* errore */

s=malloc(size);



while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return s;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

andbin
13-03-2008, 14:57
s=malloc(size);

while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return s;}Il valore di ritorno della malloc dovresti anche testarlo.

Non è sufficiente sostituire fread() a fgetc() sono due cose diverse. fread() è sempre in stdio come per fgetc/fseek/ftell.

Poi non ti sei reso conto di una cosa: nel ciclo incrementi il puntatore s e va bene. Ma poi alla fine restituisci proprio quello, che ormai punta dopo la fine del blocco di memoria!!!

xbubbax
13-03-2008, 14:59
quindi mi salvo la testa del puntatore pèrima di entrare nel while


e per quanto riguarda la malloc non ho capito che vuoi dire... ti riferisci al test nel main?

xbubbax
13-03-2008, 15:00
ho corretto il problema del puntatore, sperando di averlo azzeccato

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

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

long pos, size;
char c=0;
static char *s;

if ((pos = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, 0, SEEK_END) != 0)
return NULL; /* errore */

if ((size = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, pos, SEEK_SET) != 0)
return NULL; /* errore */

s=malloc(size);

char *temp=s;


while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return temp;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

xbubbax
14-03-2008, 09:25
up

andbin
14-03-2008, 09:52
Ci sono ancora diverse cose che non vanno e/o che comunque si possono migliorare:

a) La gestione degli errori: malloc può fallire (ritorna NULL) e in tal caso non si può certo continuare con la lettura del file. La tua readFile può a sua volta fallire (proprio perché può ritornare NULL) e quindi anche nel main andrebbe testato il puntatore ottenuto.

b) Hai usato la parola chiave 'static' per 2 variabili e per la funzione. Spero che tu abbia capito a cosa serve 'static' ... perché per un programmino del genere non serve.

c) La lettura sarebbe più efficiente farla con fread() invece di leggere singoli byte per volta.

d) C'è un'altra questione di fondo: tu leggi l'intero contenuto del file. Si presuppone che il contenuto sia "testuale" visto che poi vuoi stamparlo con printf. Già ... ma printf si aspetta una stringa terminata dal carattere nullo. E nella tua area di memoria non hai previsto la impostazione di un carattere nullo finale e tantomeno in un file di testo in genere è presente il carattere nullo.

xbubbax
14-03-2008, 11:16
a)purtroppo ancora non posso usare il main, devo scrivere solo questa funzione. Quindi cosa dovrei testare nella funzione?

b)Mi è stato imposto di usare static, non posso fare altrimenti. Fino a ieri non sapevo nemmeno cosa significasse...

c)ho provato a inserire fread ma mi da errore? come mai? devo passarle i parametri in modo diverso dalla fgetc?

d)quindi dovrei inserirlo io il carattere nullo? ora provo a farlo

andbin
14-03-2008, 11:47
a)purtroppo ancora non posso usare il main, devo scrivere solo questa funzione. Quindi cosa dovrei testare nella funzione?Scusa ma in che senso "non posso usare il main"??? Il main l'hai scritto tu, no? Dovunque chiami una funzione che può fallire, dovresti mettere un apposito test.

b)Mi è stato imposto di usare static, non posso fare altrimenti. Fino a ieri non sapevo nemmeno cosa significasse...Ahh fantastico ... chi è quel genio che ti ha detto che quel char *s va messo static???

c)ho provato a inserire fread ma mi da errore? come mai? devo passarle i parametri in modo diverso dalla fgetc?Ma ovvio ... il prototipo della funzione è tutto diverso:

--> http://www.cplusplus.com/reference/clibrary/cstdio/fread.html

d)quindi dovrei inserirlo io il carattere nullo? ora provo a farloSì ma chiaramente devi anche allocare 1 byte in più, quindi non size ma size+1. Poi visto che malloc non azzera la memoria, lo imposti tu il nullo con s[size] = '\0';

xbubbax
14-03-2008, 12:37
Quel genio è il professore:D
il main che ho scritto io non è il vero main del programma, è un main che ho fatto io per testare la funzione diciamo.

comq ho aggiustato quelle cosette, tranne la fread che farò tra un po ma mi da sempre errore...

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

static char* readFile(FILE * in){

/* Copia il file di input in una stringa e la restituisce. */

long pos, size;
char c=0;
static char *s;

if ((pos = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, 0, SEEK_END) != 0)
return NULL; /* errore */

if ((size = ftell (in)) == -1)
return NULL; /* errore */

if (fseek (in, pos, SEEK_SET) != 0)
return NULL; /* errore */

s=malloc(size+1);
s[size] = '\0';

char *temp=s;


while(c!=EOF){
c=fgetc(in);
*s=c;
s++;}
return temp;}

int main(void){

FILE *f1;
static char *s;

f1=fopen("prova.txt", "r");

s=readFile(f1);

printf("%s\n", s);

system("PAUSE");}

xbubbax
15-03-2008, 10:00
up

xbubbax
17-03-2008, 12:10
up:D