Newsgroup: it.comp.lang.c
Titolo: Da file txt a bin

Sto facendo un progetto per l'università da implementare in C ANSI.
Maledetti files.
Il mio problema è questo. Devo prendere il contenuto di un file txt
contenente autore e titolo canzone e durata e metterlo in un altro file
binario organizzato con una struttura così fatta:
struct discografia{
     char titolo[40];
     int durata;
    };
Il contenuto del file di testo è questo (in realtà contiene circa 280
canzoni):
Abba - Fernando;4.13
Abs - What You've Got;3.52
Afroman - Because I Got High;5.10
A-Ha - Stay On These Roads;4.46
A-Ha - Take On Me;3.45
Alanais Morisette - Hands Clean;4.29
Alcazar - Crying At The Discoteque;3.51
Alicia Keys - Fallin';3.17
Ecco cosa ho scritto io:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* vettore di caratteri */
struct discografia{
     char titolo[40];
     int durata;
    };
int main()
{
  FILE* ftxt;                      /* puntatore al file di testo */
  FILE* fbin;                     /* puntatore al file binario*/
  char tmp[40],                   /* stringa temporanea per formattazione
*/
  *time;                               /* punt. a char per conversione
durata   */
  struct discografia playlist,  /* Vett. di struct per memorizz. in bin */
       e[285];
  int j;
  long pos;
  int lung;
  ftxt=fopen("canzoni.txt", "r");
  fbin=fopen("canzoni.bin", "wb");
  lung=0;
  while (!feof(ftxt))
   {
    fgets(tmp,80,ftxt);                                     /* load riga
file txt in tmp */
    pos=ftell(ftxt);                                          /* controllo a
quale byte del file txt si trova il puntatore */
    lung=lung+strlen(tmp)+1;                         /* verifico quanti
caratteri ho letto finora per confrontarlo con pos */
    strtok(tmp,";");                                         /* isolamento
titolo         */
    strcpy(playlist.titolo,tmp);                       /* copia titolo nel
vettore  */
/*     Le seguenti due riga trasformano la durata da stringa nella foma m.ss
in un intero che esprime la durata della
        canzone in secondi
*/
    time=strtok(NULL,";");
    playlist.durata=(atoi(strtok(time,"."))*60) + atoi(strtok(NULL,"."));
    fwrite(&playlist,sizeof(struct discografia),1,fbin);  /*   scrivo nel
file binario   */
   }
  fclose(ftxt);
  fclose(fbin);
 / *    Ora stampo nel file binario         */
  fbin=fopen("CANZONI.BIN","rb");
  fread(&e,sizeof(struct discografia),285,fbin);
  fclose(fbin);
  for (j=0;j<285;j++)
       printf("\n%d:\n\n\n%s -- %d",j+1,e[j].titolo,e[j].durata);
  return 0;
}
Se si prova ad eseguire il tutto si vedrà che ad un certo punto la lettura
del file non va avanti e senza alcun motivo la variabile pos (ma anche un
qualunque contatore i) viene azzerato dall'esecuzione della riga:
    fgets(tmp,80,ftxt);
Insomma se lo lancio non funziona. Soprattutto con il file di testo completo
delle 280 canzoni.
Qualcuno può spiegarmi quale è il problema?


Di problemi ce ne sono diversi....
Il piu' grave e' che hai dimensionato il tmp a 40 caratteri,
mentre nel file ci sono linee piu' lunghe (considerando anche
il newline) e usi il fgets con 80 come limite.
per esempio la linea
Alcazar - Crying At The Discoteque;3.51
e' lunga 40 caratteri (considerando che fgets legge anche il fine linea)
e quindi occupa 41 caratteri e quindi "sfora" lo spazio allocato
per tmp.
Poi secondo me dovresti evitare l'uso della funzione strtok:
1) Non e' thread-safe (ma questo ora non importa)
2) considerando la struttura fissa della riga ti basta usare
    strchr che e' ,molto piu' semplice e leggibile. Anche perche'
    alcune chiamate a strtok che fai sono abbastanza inspiegabili,
    come ad esempio la seconda strtok con il ";" e le due con il "."
3) Errore nella riga in cui invochi due volte strtok !!
    quando tu scrivi z = x + y; il compilatore e' libero di valutare
    prima x o y come piu' gli aggrada. E' chiaro che scrivendo
    qualcosa tipo strtok()+strtok() e' quindi un grave errore, perche'
    il valore della strtok() dipende dall'ordine nel quale sono
    invocate!
Io poi metterei anche un controllo subito dopo la fgets() per
evitare di processare stringhe vuote. Infatti se per errore
il tuo file di testo contiene un "a-capo" di troppo (cosa facile in un 
file di testo editato a mano, per esempio)
il programma facilmente si pianta.
Infine, nel ciclo finale leggi max 285 record, (e questo va bene, nel 
senso che se ce ne sono meno non vengono letti) ma poi ne scrivi
comunque sempre 285: se ne hai letti meno questo fara' probabilmente
piantare il programma. (puoi usare il fatto che la fread restituisce
il numero di items effettivamente letti).
ciao,
g.

 fwrite(&playlist,sizeof(struct discografia),1,fbin); 
Anche questa fwrite secondo me č inutile.. forse č buona, ma io avrei
utilizzato una fprintf.
Ti ho rifatto il programma, come secondo me sarebbe meglio fare e facendo
una qualche modifica al tuo codice adesso funziona (l'ho testato da me in
locale).
Spero ti possa essere d'aiuto.
---------------------------------------------
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <tchar.h>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
struct discografia
{
	char titolo[256];
	float durata;
};
struct discografia lista;
int main (void)
{
	FILE *fh_txt;
	FILE *fh_bin;
	char riga[256];
	char *token_sinistra;
	char *token_destra;
	char *appo;
	char path_file_txt[256] = "c:\\canz.txt";
	char path_file_bin[256] = "c:\\canz.bin";
	fh_txt = fopen(path_file_txt, "r");
	fh_bin = fopen(path_file_bin, "wb");
	fseek(fh_txt, 0, SEEK_SET);
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		appo = (char *)strtok( riga, ";" );
		strcpy(lista.titolo, appo);
		//printf("Titolo: %s\n", lista.titolo);
		appo = strtok( NULL, "\n" );
		lista.durata= atof(appo);
		//printf("Durata: %f\n", lista.durata);
		fprintf(fh_bin, "%s;%f\n", lista.titolo, lista.durata); 
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_txt) )
	{
		fgets(riga, 256, fh_txt);
		printf("Riga: %s", riga);
	}
}
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

Ciao, scusami il sorgente di prima è sbagliato.. ecco il sorgente giusto
Ri-ciao,
SoloOcchi
--------------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <stdio.h>
struct discografia
{
	char titolo[256];
	float durata;
};
struct discografia lista;
int main (void)
{
	FILE *fh_txt;
	FILE *fh_bin;
	char riga[256];
	char *appo;
	char path_file_txt[256] = "c:\\canz.txt";
	char path_file_bin[256] = "c:\\canz.bin";
	fh_txt = fopen(path_file_txt, "r");
	fh_bin = fopen(path_file_bin, "wb");
	fseek(fh_txt, 0, SEEK_SET);
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		appo = (char *)strtok( riga, ";" );
		strcpy(lista.titolo, appo);
		//printf("Titolo: %s\n", lista.titolo);
		appo = strtok( NULL, "\n" );
		lista.durata= atof(appo);
		//printf("Durata: %f\n", lista.durata);
		fprintf(fh_bin, "%s;%f\n", lista.titolo, lista.durata); 
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_bin) )
	{
		fgets(riga, 256, fh_bin);
		printf("Riga: %s", riga);
	}
	fclose(fh_bin);


 #define WIN32_LEAN_AND_MEAN
:-)
Tommy

 Tommy
Comunque, rileggendo quello che ha scritto Giovanni, ha ragione se
incontro una riga vuota il programma crasha, ecco il proggie fixato :)
Ciao,
SoloOcchi
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <tchar.h>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
struct discografia
{
	char titolo[256];
	float durata;
};
struct discografia lista;
int main (void)
{
	FILE *fh_txt;
	FILE *fh_bin;
	char riga[256];
	char *appo;
	char path_file_txt[256] = "c:\\canz.txt";
	char path_file_bin[256] = "c:\\canz.bin";
	int a;
	fh_txt = fopen(path_file_txt, "r");
	fh_bin = fopen(path_file_bin, "wb");
	fseek(fh_txt, 0, SEEK_SET);
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		if ( strcmp(riga, "\n") == 1 )
		{
			appo = (char *)strtok( riga, ";" );
			strcpy(lista.titolo, appo);
			//printf("Titolo: %s\n", lista.titolo);
			appo = strtok( NULL, "\n" );
			lista.durata= atof(appo);
			//printf("Durata: %f\n", lista.durata);
 
			fprintf(fh_bin, "%s;%f\n", lista.titolo, lista.durata); 
		}
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	fseek(fh_bin, 0, SEEK_SET);
	while ( !feof(fh_bin) )
	{
		fgets(riga, 256, fh_bin);
		printf("Riga: %s", riga);
	}
	fclose(fh_bin);


 	}
Questo ciclo rischia fortemente di stampare l'ultima
"riga" del file due volte.... fai la prova...
ciao,
g.

 g.


 questo problema?
Il motivo penso sia che la feof viene riconosciuta solo
se stai cercando di leggere oltre la fine del file, ma
se l'ultima linea e' terminata da un newline, allora la
fgets si ferma al newline, il feof non viene riconosciuto,
e al ciclo dopo non viene letto nulla (e quindi la riga non
viene modificata)
Non so se c'e'e un metodo standard particolarmente furbo
per evitare la cosa. (probabilmente c'e' piu' di un modo, che
dipende anche da quello che stai facendo).
per esempio puoi fare qualcosa di questo tipo:
while ( !feof(fh_bin) )
{
   riga[0]=0;
   fgets(riga, 256, fh_bin);
   if (riga[0]==0) break;
   printf("Riga: %s", riga);
}
Oppure puoi fare un controllo sul valore ritornato
da fgets, che dovrebbe essere uguale a NULL se non viene
letto nulla e a riga se invece viene letto almeno 1 carattere.
quindi il ciclo diventerebbe:
while (fgets(riga,256,fh_bin))
   printf("Riga = %s",riga);
ciao,
g.

Ho fatto qualche correzione.. adesso alla fine il programma crasha, non
capisco perchč.
Poi ho un altra domanda.. quando leggo il file bin di lista.duiarata non
ve ne č traccia.. come mai?
Grazie
struct discografia
{
	char titolo[256];
	int durata;
};
struct discografia lista;
int main (void)
{
	FILE *fh_txt;
	FILE *fh_bin;
	char riga[256];
	char *appo;
	char path_file_txt[256] = "c:\\canz.txt";
	char path_file_bin[256] = "c:\\canz.bin";
	fh_txt = fopen(path_file_txt, "r");
	fh_bin = fopen(path_file_bin, "wb");
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		if ( strcmp(riga, "\n") == 1 )
		{
			appo = strtok( riga, ";" );
			strcpy(lista.titolo, appo);
 
			appo = strtok( NULL, "\n" );
			lista.durata= atoi(appo);
			fwrite(&lista, sizeof(struct discografia), 1, fh_bin); 
		}
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	while ( !feof(fh_bin) )
	{		
		riga[0] = 0;
		fread(riga, sizeof(struct discografia), 1, fh_bin);
		if (riga[0]==0) break;
		printf("Riga: %s\n", riga);
	}
	fclose(fh_bin);
	return 1;


 ve ne č traccia.. come mai?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct discografia
{
	char titolo[256];
	int durata;
};
struct discografia lista;
int main (void)
{
	FILE* fh_txt = NULL;
	FILE* fh_bin = NULL;
	char* appo   = NULL;
   char riga[256];
   char path_file_txt[256] = "c:/canz.txt";
	char path_file_bin[256] = "c:/canz.bin";
// Se "c:/canz.txt" non esiste fh_txt e' NULL
// e feof() fa Boom.
// ------------------------------------------
	fh_txt = fopen(path_file_txt, "r");
// Non sarebbe male anche un controllo su fh_bin
// visto che pure lui puo essere NULL.
// ---------------------------------------------
	fh_bin = fopen(path_file_bin, "wb");
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		if ( strcmp(riga, "\n") == 1 )
		{
			appo = strtok( riga, ";" );
			strcpy(lista.titolo, appo);
 
			appo = strtok( NULL, "\n" );
      // ????? atoi per una durata scritta come 4.13?
      // Un atof non sarebbe piu' appropriato?
      // Ovviamente dopo aver modificato la struttura...
      // ------------------------------------------------
			lista.durata= atoi(appo);
      // ...e mettere un controllino per verificare il
      // risultato di fwrite()?
      // ----------------------------------------------------
			fwrite(&lista, sizeof(struct discografia), 1, fh_bin); 
		}
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	while ( !feof(fh_bin) )
	{		
		riga[0] = 0;
   // ???? e qua cosa vorresti fare? leggere una struttura
   // e metterla in un array di char? Sicuro che e' quello
   // che volevi?
   //
   // Gia' il fatto che la printf stampi qualcosa di sensato
   // e' un discreto colpo di culo (dovuto al fatto che il
   // dato accodato in quanto memorizzato sul file inizia
   // per 0x0).
   // ----------------------------------------------------
		fread(riga, sizeof(struct discografia), 1, fh_bin);
   // Brutto modo di uscire dal ciclo...
   // ----------------------------------
		if (riga[0]==0) break;
		printf("Riga: %s\n", riga);
	}
	fclose(fh_bin);
	return 1;
}
HTH
C:\>iao Angelo
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 C:>iao Angelo
Grazie ancora ciao,
SoloOcchi


Per completezza e per chi aveva posto la domanda, posto il codice finito e
funzionante:
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
struct discografia
{
	char titolo[256];
	float durata;
};
struct discografia lista;
int main (void)
{
	FILE *fh_txt;
	FILE *fh_bin;
	char riga[256];
	char *appo;
	char path_file_txt[256] = "c:\\canz.txt";
	char path_file_bin[256] = "c:\\canz.bin";
	fh_txt = fopen(path_file_txt, "r");
	fh_bin = fopen(path_file_bin, "wb");
	while ( !feof(fh_txt) )
	{
        fgets(riga, 256, fh_txt);
		if ( strcmp(riga, "\n") == 1 )
		{
			appo = strtok( riga, ";" );
			strcpy(lista.titolo, appo);
 
			appo = strtok( NULL, "\n" );
			lista.durata= atof(appo);
			fwrite(&lista, sizeof(struct discografia), 1, fh_bin); 
		}
	}
 
	fclose(fh_txt);
	fclose(fh_bin);
// LEGGO IL FILE BINARIO
	fh_bin = fopen(path_file_bin, "rb");
	while (fread(&lista, sizeof(struct discografia), 1, fh_bin))
	{		
		printf("Titolo: %s durata: %f\n", lista.titolo, lista.durata);
	}
	fclose(fh_bin);
	return 1;


 meglio un intero.. magari se lo capisci me lo spieghi pure a me :)
Cerchiamo di capirci ! Io ho assunto che la durata delle
canzoni tipicamente si rappresenti come "minuti e secondi".
Se la durata espressa come 4.13 (o 5.56 o 3.31) rappresenta quindi
minuti.secondi (cioe' 4 minuti e 13 secondi) allora continuo
a pensare che sia meglio leggerla come due interi e poi
convertirla in un unico intero che mi dia la durata in secondi
(cioe' 4*60+13).
Questo perche' se la leggi come un float cioe' come 4.13 ovvero
come 4+13 centesimi, allora poi potresti correre il rischio
di addizionare questi numeri float per avere, per esempio,
il totale di durata di un disco.
E' chiaro che se 4.23 e 2.45 sono minuti e secondi allora
  4.23 + 2.45 = 7.08 (minuti e secondi) mentre se hai convertito
in float ottieni 6.68 che chiaramente non ha senso.
Quindi se lo memorizzi in un float (che ovviamente conterra'
solo una approssimazione di 4.13) dopo dovrai' fare i
salti mortali per riconvertire il numero in minuti e secondi
per poter farci degli eventuali calcoli sopra.
Per questo motivo mi sembra piu' opportuno usare una rappresentazione
interna coerente col dato da rappresentare (che e' difatto un intero).
Ciao,
g.

 per poter farci degli eventuali calcoli sopra.
Premesso che il tuo discorso e' corretto, direi che usare modf()
e moltiplicare per 100 la parte frazionaria, non e' quello che
si puo' definire "fare i salti mortali" :-)
Tanto piu' che l'eleborazione da fare su quanto ottenuto e'
uguale per entrambi i casi.
Quello che non si fa prima lo si puo' fare dopo (e forse in
questo caso e' leggermente piu' semplice farlo dopo).


 questo caso e' leggermente piu' semplice farlo dopo).
E' ovvio che uno puo' fare come preferisce, pero' se uno
comincia ad usare i float dove "logicamente" andrebbero usati
gli interi allora deve fare molta attenzione.
Per esempio, supponiamo che tu abbia bisogno dei minuti e dei
secondi e come suggerito tu voglia estrarli usando la funzione modf()
(la funzione double modf(double x, double *iptr) si chiama passandogli
un valore x e restituisce la parte frazionaria mentre memorizza la
parte intera (come double!) nella variabile puntata da iptr).
(bisogna ricordarsi di includere math.h)
Nel caso intero avremmo:
int durata_in_secondi, minuti, secondi;
minuti = durata_in_secondi / 60;
secondi= durata_in_secondi % 60;
nel caso float avremmo qualcosa di questo tipo
// supponiamo che la canzone duri 3.26 (3 minuti e 26 secondi)
float fdurata;
double dsec, dmin;
int secondi, minuti;
// abbiamo
fdurata  = 3.26; // per esempio
dsec = modf((double)fdurata, & dmin);
minuti = (int) dmin;
secondi = (int) (100.0 * dsec); // come suggerito
printf("la durata = %d min %d sec\n",minuti,secondi);
Effettivamente non abbiamo dovuto fare i salti mortali,
pero' il risultato che questo programma stampa (3 min 25 sec) e'
sbagliato !
Cioe' per effetto della rappresentazione dei numeri in floating
point abbiamo perso un secondo....
E' chiaro che possiamo ovviare a questo problema aggiungendo una piccola
costante:
secondi = (int) (0.01 + 100.0*dsec);
Tu sicuramente avevi presente questo problema, ma l'originatore
del thread mi sembra che stia imparando a programmare, quindi
ho pensato fosse meglio la soluzione con gli interi, che non nasconde
certi trabocchetti.
ciao,
g.

 Tu sicuramente avevi presente questo problema,
Mi giudichi meglio di quello che sono :-)
C:\>iao Angelo
