Pagina 1 di 4 1 2 3 ... ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 34
  1. #1

    Riempire struct da file di testo

    Salve a tutti!!

    Premetto che ho cominciato a studiare informatica da poco più di un anno e che quindi non ho una grande esperienza.
    Da poco nel mio corso abbiamo cominciato a studiare i file sia nel C e sia nel C++, a dirla tutta stiamo ancora agli inizi ma per esigenze lavorativi avrei bisogno di farmi un programma che da un file di testo riesca ad alimentare uno struct.

    Mi spiego meglio, ho un file txt pieno di righe come questa:

    20120102;000001;00001;4;2805

    Ho quindi creato uno struct a seconda del tipo di informazione:
    codice:
    struct kk
    {
       char data[8];
       int reg;
       int cont;
       int tipo;
       char conto[10];
    };
    Ora però non so come far capire al programma che dopo ogni punto e virgola deve mettere il valore nel variabile successiva, ho provato a fare una roba simile ma non funziona:
    while(fscanf(fp,"%s,%d,%,d,%d,%s ;",pop.data,pop.reg,pop.cont,pop.tipo,pop.conto)!= EOF)

    Il codice intero è ancora un cantiere, in realtà per ora sto solo facendo delle prove e diciamo che questo è l'ultimo ostacolo che mi è rimasto, ovviamente se riuscite a darmi anche una spiegazione in modo da farmi capire come si affronta un caso simile mi farete molto contento!!

    Grazie a tutti in anticipo.

  2. #2
    Direi più che altro:
    codice:
    while(fscanf(fp,"%s;%d;%d;%d;%s",pop.data,pop.reg,pop.cont,pop.tipo,pop.conto)==5)
    Se noti, al posto delle virgole ho messo dei punti e virgola (dato che i tuoi campi sono appunto separati da punti e virgola), e al posto di !=EOF ho messo ==5. Questo perché fscanf restituisce il numero di campi letti correttamente, e in caso di errore di lettura (ad esempio, una stringa invece di un numero) è importante fermare la lettura invece di andare avanti con risultati scorretti.

    Inoltre, al posto di %s dovresti usare %Ns, dove N è il numero massimo di caratteri da leggere - anche qui è importante, dato che altrimenti rischi un buffer overflow nelle stringhe della struttura che stai popolando.

    In ogni caso, ti rimando alla documentazione della fscanf, che spiega esattamente come funziona e come viene gestita la stringa di formato.

    ---

    Tra parentesi, il linguaggio di riferimento va indicato nel titolo, qui lo aggiungo io, in futuro ricordatene.
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Originariamente inviato da MItaly
    Direi più che altro:
    codice:
    while(fscanf(fp,"%s;%d;%d;%d;%s",pop.data,pop.reg,pop.cont,pop.tipo,pop.conto)==5)
    Se noti, al posto delle virgole ho messo dei punti e virgola (dato che i tuoi campi sono appunto separati da punti e virgola), e al posto di !=EOF ho messo ==5. Questo perché fscanf restituisce il numero di campi letti correttamente, e in caso di errore di lettura (ad esempio, una stringa invece di un numero) è importante fermare la lettura invece di andare avanti con risultati scorretti.

    Inoltre, al posto di %s dovresti usare %Ns, dove N è il numero massimo di caratteri da leggere - anche qui è importante, dato che altrimenti rischi un buffer overflow nelle stringhe della struttura che stai popolando.

    In ogni caso, ti rimando alla documentazione della fscanf, che spiega esattamente come funziona e come viene gestita la stringa di formato.

    ---

    Tra parentesi, il linguaggio di riferimento va indicato nel titolo, qui lo aggiungo io, in futuro ricordatene.
    Ops sorry per il titolo, non frequentando il forum non conosco le regole, la prossima volta cercherò di ricordarmelo!!

    Vediamo se ho capito, il punto e virgola nel codice serve a dirgli che dopo ogni punto e virgola c'è un nuovo valore, abbastanza facile, mentre il ==5 si traduce in "finchè riempi 5 campi continua altrimenti fermati (e si fermerà alla fine del file visto che troverà 0 campi)", non capisco però come fa a capire di andare a capo ogni volta (sempre che non sia una proprietà della funzione fscanf).
    Sono abbastanza sicuro della bontà file che sto importando quindi non credo mi possa servire di specificare quanti caratteri deve importare a campo, ad ogni modo grazie comunque per la dritta, non si sa mai!
    Ho dato un'occhiata veloce alla guida che mi hai linkato e penso sia molto esaustiva, grazie mille per avermela postata.

  4. #4
    Originariamente inviato da bad_hunter_666
    Vediamo se ho capito, il punto e virgola nel codice serve a dirgli che dopo ogni punto e virgola c'è un nuovo valore, abbastanza facile,
    Esatto; se invece del punto e virgola trova un altro carattere, smette di leggere, per cui ti ritornerà un valore diverso da 5 (e così sai che il file è formattato male).
    però come fa a capire di andare a capo ogni volta (sempre che non sia una proprietà della funzione fscanf).
    Implicitamente la fscanf "mangia" tutto il whitespace che incontra prima di iniziare a leggere i campi specificati, e i ritorni a capo (così come gli spazi e le tabulazioni) sono considerati whitespace.
    Sono abbastanza sicuro della bontà file che sto importando quindi non credo mi possa servire di specificare quanti caratteri deve importare a campo, ad ogni modo grazie comunque per la dritta, non si sa mai!
    Diciamo che in genere è bene non fidarsi dell'input esterno, specie, come in questo caso, se un input errato può condurre ad un buffer overflow (=>crash casuali o strani malfunzionamenti del programma).
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Qualcosa non va, posto intanto il codice (il sorgente si chiama "esperimenti" quindi è un casino):

    codice:
    #include<iostream>
    #include<string.h>
    #include<fstream>
    
    using namespace std;
       struct kk
        {
       char data[8];
       int reg;
       int cont;
       int tipo;
       char conto[10];
       };
    FILE *fp;
    void aggiungitxt(char nome[]);
    int main()
    {
        fstream fout;
        kk pop;
        char nome[20];
        char filename[20];
        int vettore[10];
        int n,i=0,j,choice;
        cout<<"esperimento 1 o 2?"<<endl;
        cin>>choice;
        cin>>filename;
        aggiungitxt(filename);
        cout<<filename<<endl;
        
        fp=fopen(filename,"r");
        
        if (choice==1)
        {
         while(fscanf(fp,"%d",&j)!=EOF)  
         {
          vettore[i]=j;                           
          i++;
          }              
         fclose(fp);
         j=i;
        for(i=0;i<j;i++)
        cout<<vettore[i]<<endl;
        }
        else
        {
        while(fscanf(fp,"%s;%d;%d;%d;%s",pop.data,pop.reg,pop.cont,pop.tipo,pop.conto)==5)
        i++;
        cout<<pop.data<<endl;
        cout<<pop.reg<<endl;
        cout<<pop.cont<<endl;
        cout<<pop.tipo<<endl;
        cout<<pop.conto<<endl;
        fclose(fp);
        }
        system("pause");
         return 0; 
    }
    
    void aggiungitxt(char nome[])
    {
       int i;  
       i=strlen(nome);  
       nome[i]='.';
       nome[i+1]='t';
       nome[i+2]='x';
       nome[i+3]='t';
       nome[i+4]='\0';
         
         }

    Partiamo con le spiegazioni, la funzione "aggiungitxt" serve soltanto ad aggiungere .txt al nome del file che carichi, niente di importante quindi, il programma appena avviato chiede quale esperimento vuoi fare, il primo carica in un vettore di interi un file con soli numeri uno per riga, nessun problema funziona.
    Il secondo è invece quello incriminato, ti posto l'output:


    Ho provato a fare delle prove mettendo il numero di caratteri che deve leggere per campo, se metto al primo campo il suo numero preciso e cioè 8 il programma crasha e si chiude, invece mettendo 7 o 9 riesce a caricare bene quel campo (col 9 non mi spiego perchè) ma ovviamente rovina tutti quelli dopo.

  6. #6
    Ah giusto, sono io che sono scemo. %s va avanti a leggere caratteri finché non trova del whitespace, per cui tira su nella prima stringa tutto quanto fino al newline. Correggi la stringa di formato della fscanf con:
    codice:
    "%[^;];%d;%d;%d;%[^;\n]"
    ([^;] indica "tutti i caratteri tranne il ;")
    Amaro C++, il gusto pieno dell'undefined behavior.

  7. #7
    Niente, non gli piace nemmeno così:




    Ho provato pure a mettere una s davanti a [^;] ma l'output non cambia.

    Ti lascio la riga:

    codice:
    while(fscanf(fp,"[^;];%d;%d;%d;%[^;\n]",pop.data,pop.reg,pop.cont,pop.tipo,pop.conto)==5)
    Aggiungo che uso il devc++ per compilare.

  8. #8
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,462
    Per i valori numerici devi indicare il puntatore all'elemento

    &pop.reg

    &pop.cont

    &pop.tipo
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  9. #9
    Originariamente inviato da oregon
    Per i valori numerici devi indicare il puntatore all'elemento

    &pop.reg

    &pop.cont

    &pop.tipo
    Giusto!!

    Ho poi aggiunto una s dopo il [^;]

    Prima era così

    codice:
    "%[^;];%d;%d;%d;%[^;\n]"
    Ora è così

    codice:
    "%[^;]s;%d;%d;%d;%[^;\n]s"
    Il risultato di queste modifiche è che almeno il primo campo lo prende bene:


  10. #10
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,462
    A patto che sia

    char data[9]

    per il terminatore della stringa e che sia

    while(fscanf(fp, "%[^;];%d;%d;%d;%[^;\n]", pop.data, &pop.reg, &pop.cont, &pop.tipo, pop.conto)==5)

    mi sembra che vada
    No MP tecnici (non rispondo nemmeno!), usa il forum.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.