[Risolto] Legge da file

Return89
Ciao a tutti.. è molto urgente quindi se sapete come aiutarmi ve ne sarei grato

devo leggere da un file di testo (.txt) in questa forma:
Nome Cognome : interesse1, interesse2, interesse3, ecc ecc

non so perchè ma mi sono bloccato nella lettura da file.

la struttura l'ho impostata così:
typedef char interesse[20];

typedef struct{
char nome[15];
char cognome[15];
interesse interes[18];
}persone;


while(fscanf(fp,"%s %s :",persone.nome,persone.cognome)!=EOF){ ...
con questo ciclo leggo il nome ed il cognome..cosa devo inserire dentro per caricare correttamente i vari interessi (separati da spazio/virgola) ??


Grazie mille a chiunque mi aiuti ^^

Risposte
vict85
Per prima cosa elimina la parola urgente dal titolo.

Dopo di che puoi chiedere il numero di interessi e chiederne uno per volta oppure decidere un modo per segnalare al programma che non ci sono più altri interessi.

Return89
Devo leggere da file quindi non posso far immettere da tastiera a priori il numero di interessi..
come faccio a leggere (con fscanf/fgets/ecc) fino a quando ci sono valori?

Grazie ^^

apatriarca
Gli interessi possono contenere spazi? Normalmente si legge una riga per volta e poi si estraggono i valori dalla stringa cercando i separatori con funzioni come strchr..

Return89
strchr? ma è una funzione del c o c++?
sapresti spiegarmi come si implementa e a cosa serve? Grazie mille!

apatriarca
È una funzione che si trova sia nel C, che nel C++. Trova la successiva ricorrenza di un particolare carattere in una stringa. Ma forse soluzioni più semplici o alternative sono possibili.. Non hai risposto alla mia domanda sulla possibilità di avere spazi all'interno delle stringhe degli interessi..

Return89
Ah non avevo capito fosse una domanda..
comunque no, non ci possono essere spazi, cioè ogni interesse è definito da un'unica parola!

Quindi utilizzo una semplice fgets per leggere le righe in un vettore qualsiasi e poi le spezzetto con quella funzione? Come dovrei implementarla?

Grazie ancora!

Return89
Ho studiato la funzione strchr ma onestamente mi sembra una soluzione troppo complicata..

come potrei fare sennò? Ho esaurito le forze mentali..

nessuno.nobody
To, ti ho fatto una versione che legge una sola riga. Ci metti due secondi ad adattarlo a leggere tutto
#include <stdio.h>
#include <stdlib.h>

typedef char interesse[20];

typedef struct{
char nome[15];
char cognome[15];
interesse interes[18];
} persone;

#define die(...) exit(fprintf(stderr,##__VA_ARGS__));

int main(int argc, char *argv[])
{
	FILE *fp = fopen(argv[1],"r");
	int c, interestsCount = 0, interestLenght = 0;
	persone persona;
	
	if(!fp)
		die("Errore nell'apertura di file.txt");

	//Leggo una sola riga, per leggerle tutte cicla per ogni \n

	if(2 != fscanf(fp,"%s %s :",&persona.nome,&persona.cognome))
		die("Formato file non corretto");

	//il puntatore ora è avanti, leggo fino al termine della riga ed estraggo gli interessi
	do
	{
		c = fgetc(fp);
		if(c == ' ')
		{
			persona.interes[interestsCount][interestLenght-1] = '\0';
			++interestsCount;
			interestLenght = 0;
			continue;
		}
		persona.interes[interestsCount][interestLenght++] = c;
	}
	while(c != '\n'); //se sei nel ciclo, controlla anche che non arrivi a EOF
	fclose(fp);

	printf("Nome: %s\nCognome: %s\n",persona.nome,persona.cognome);
	printf("Interessi: ");
	for(c=0;c<interestsCount;++c)
		puts(persona.interes[c]);

	return 0;
}

apatriarca
Credo che la soluzione più semplice sia quella di leggere una riga per volta con fgets (immagino che ogni riga sia formattata come hai scritto) e quindi usare sscanf per leggere i valori che ti interessano.. Ci sono alcuni dettagli tecnici un po' delicati per usare questo sistema per cui provo a scriverti un codice di esempio..

Return89
Okk grazie!

Grazie nobody, credo di aver capito quella funzione.

apatriarca
Ok, il codice di esempio è il seguente.. Legge da console ma la lettura da file è uguale (uso infatti fgets che è uguale in entrambi i casi). Unico difetto è che è necessario avere uno spazio tra gli interessi e la virgola o non funziona.. Per farlo funzionare è necessario cambiare la stringa di formato nello sscanf. %n è quello che fa funzionare il tutto e restituisce il numero di caratteri letti dalla funzione.
#include <stdio.h>

int main(int argc, char *argv[])
{
    char riga[1024] = "";
    char nome[64] = "";
    char cognome[64] = "";
    char interesse[64] = "";
    int n = 0, d = 0;
    while(fgets(riga, sizeof(riga), stdin)) {
        sscanf(riga, "%63s %63s :%n", nome, cognome, &n);
        printf("Nome: %s\nCognome: %s\n", nome, cognome);
        while (sscanf(riga+n, " %63s ,%n", interesse, &d) > 0) {
            printf("%s\n", interesse); n += d;
        }
    }
    return 0;
}

Return89
Ho provato ad adattarla ma penso di aver dimenticato qualcosa (sono sui libri da 8 ore e non capisco più nulla..)
sscanf legge da "riga" una parola (fino allo spazio) e copia il valore su "nome", giusto? E' questo il funzionamento?

apatriarca
sscanf è esattamente come scanf ma invece di leggere da console, legge i caratteri da una stringa. Stringa che nel mio caso ho letto da fgets ma può venire da qualsiasi fonte. Siccome sscanf non memorizza l'ultima posizione letta dalla chiamata precedente è necessario leggere tale posizione con %n e quindi spostare la posizione iniziale di lettura del numero corretto di posizioni. Questo è più o meno quello che fa quel codice usando le variabili n e d. Quando devi consegnare questo esercizio? Forse dovresti prenderti una pausa..

Return89
Okk perfetto, credo di aver capito..
cmq si, hai ragione, una pausa ci vorrebbe..

grazie mille!

Return89
Rieccomi...
Avevo abbandonato questo "esercizio" per dedicarmi ad altro, ma poco fa ho deciso di riprenderlo per risolverlo una volta per tutte!

Ho preso come esempio un file di testo costituito da:
nome : interesse
(solo un interesse per adesso, poi la lettura di altri interessi penso di farla mediante una lista di liste..ma ci lavoro dopo, intanto risolvo la lettura di un interesse).

string è la stringa su cui copio ogni singola riga. Poi ho pensato di copiare il contenuto delle due parti in altre due stringhe ed infine le copio sui campi degli elementi della lista.
La lista ha la seguente struttura:
typedef struct{
char nome[15];
char interesse[15];
}element;

typedef struct list_element{
element value;
struct list_element *next;
}item;
typedef item* list;

nella funzione di caricamento da file ho quindi inserito questo ciclo:
while(fgets(string,100,fp)!=NULL){
for(k=0;k<30&&string[k]!=':';k++){
nom[k]=string[k];
}
for(j=0,k=k+2;k<30&&string[k]!='\n';k++,j++){
interes[j]=string[k];
}
strcpy(e.nome,nom);
strcpy(e.interesse,interes);
e.nome[strlen(e.nome)-2]='\0';
e.interesse[strlen(e.interesse)-1]='\0';
l=insord(e,l);
}

però ancora non funziona bene. Sono sicuro che il problema sia dato dal fatto che leggendo 100 caratteri ho una stringa "sporca" e piena di simboli, quindi quando provo ad estrasse la seconda parola mi da qualche problema (ho provato un sacco di condizioni, con risultati differenti ma senza risolvere il problema).
Sono anche sicuro che sto commettendo un errore banale, per questo chiedo a voi che la vedete da fuori di dirmi cosa sbaglio..


Grazie mille!

nessuno.nobody
Inizializza le stringhe che usi a '\0'.
In maniera che anche se le riempi di caratteri validi non per la totaltà della lunghezza - 1 , sei sicuro che questa sarà terminata correttamente dal nullbyte.
Puoi usare la funzione memset per inizializzare la stringa a 0.
Poi vabbè ci sono altre cosette che non dovresti usare, come la strcpy che anche lei è il male in quanto non fa controlli sulla lunghezza dei dati, e dovresti usare quindi al suo posto la strncpy, ma per ora sorvola.
Prima occupati di far funzionare le cose inizializzando tutto.

Comunque, nel sorgente che ti ho pubblicato qualche risposta fa, c'è il modo funzionante per estrarre il nome e tutti gli interessi da una sola riga.
Puoi adattare quello ed hai vinto

Return89
Edit: risolto..
Andavano riazzerati i contatori, inizializzati a '\0' il resto dei campi non utilizzati ed il gioco è fatto

Grazie a tutti, in particolare a nobody :)

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.