glaza Δημοσ. 28 Μαΐου 2011 Δημοσ. 28 Μαΐου 2011 Θέλω να έχω ένα αρχείο με όνομα, κωδικό και κάποια άλλα πεδία...Οταν είναι να γραψω στο αρχείο όλα είναι τέρμα απλά και κατανοητά, η κάθε προσθήκη γίνεται αμεσως μετά από την επόμενη, οταν ομως θέλω να αλλάξω μια συγκεκριμένη καταχώρηση ή γενικά να τροποποιήσω συγκεκριμένο πεδίο στο αρχείο, αυτό πώς γίνεται ? πχ. ονομα : Γιαννης κωδικός : 001 ονομα : Γιωργος κωδικός : 002 ονομα : Χρηστος κωδικός : 003 αν θέλω να αλλάξω το όνομα Γιώργος -> Βασίλης, πώς το κάνω ?
migf1 Δημοσ. 28 Μαΐου 2011 Δημοσ. 28 Μαΐου 2011 Υπάρχουν διάφορες υλοποιήσεις. Μπορείς να χρησιμοποιήσεις τη συνάρτηση fread() για να διαβάσεις ΟΛΟΚΛΗΡΟ το αρχείο σε ένα κατάλληλο buffer στο πρόγραμμά σου, να αλλάξεις ότι είναι να αλλάξεις στο buffer και κατόπιν να γράψεις από την αρχή ολόκληρο το buffer στο αρχείο σου με τη συνάρτηση fwrite() (να κάνεις δηλαδή overwrite όλα τα προηγούμενα περιεχόμενα του αρχείου σου με τα καινούρια). Αν θέλεις να το κάνεις πιο "προχωρημένο" μπορείς να διαβάζεις και να γράφεις μεμονωμένα κομμάτια (πεδία) του αρχείου σου, χρησιμοποιώντας μικρότερα και πιο εξειδικευμένα buffers για τα πεδία σου και συναρτήσεις όπως τις: ftell() και fseek() ή ακόμα και τις: fgetpos() και fsetpos(). Έτσι όμως "σοβαρεύουν" τα πράγματα, αφού εν πολλοίς θα πρέπει να γνωρίζεις εκ των προτέρων τα μήκος του κάθε πεδίου σου σε bytes ( με χρήση του τελεστή: sizeof() ) και να φροντίζεις οι νέες τιμές να μην ξεπερνάνε σε μήκος το μήκος των προηγούμενων τιμών. Μια "εύκολη" λύση σε αυτή την περίπτωση είναι να ορίζεις εξαρχής μέγιστο μήκος για το κάθε πεδίο, κι αν η τιμή του είναι μικρότερη (χαρακτηριστικό παράδειγμα εδώ είναι τα πεδίου τύπου string) να γεμίζεις τον περισσευούμενο χώρο με μια default τιμή για το κάθε byte, πριν αποθηκεύσεις τα πεδία στο αρχείο. Με αυτό τον τρόπο ξέρεις εκ των προτέρων πόσο είναι το μέγιστο πλήθος αποθηκευμένων bytes για κάθε πεδίο σου μέσα στο αρχείο, και φροντίζεις να μην το ξεπερνάς. Μια άλλη λύση είναι μπροστά από κάθε σου πεδίο να αποθηκεύεις ξεχωριστά το μέγεθος που καταλαμβάνει το πεδίο (γενικώς υπάρχουν διάφορες τεχνικές ). Υπάρχουν κι άλλοι τρόποι, όπως για παράδειγμα να "σπας" το κάθε πεδίο της δομής σε ξεχωριστά strings (π.χ. με τη συνάρτηση strtok() ) και να τα αποθηκεύεις σε γραμμές ( με τη συνάρτηση: fputs() ) με κάποιον default διαχωριστικό χαρακτήρα ανάμεσά στα strings της κάθε γραμμής, ώστε να διαβάζεις κατόπιν το αρχείο σου πάλι ανά γραμμές (με τη συνάρτηση: fgets() ) και να βρίσκεις το επιθυμητό πεδίο/string. Και πάλι όμως πρέπει να σιγουρέψεις πως ο κώδικά σου δεν ξεπερνάει το μέγιστο μήκος του κάθε πεδίου/string. Για αρχή πάντως, σου προτείνω να δοκιμάσεις τον 1ο τρόπο
παπι Δημοσ. 28 Μαΐου 2011 Δημοσ. 28 Μαΐου 2011 >typedef struct tagElement { char pszZero1[5]; // space gia "Name:" char pszName[25]; // space gia to onoma char pszZero2[3]; // space gia "ID:" char pszID[12]; // space gia id char Zero3; // space gia \n }Element; int CreateElement(Element *elem,const char *name, int id) { if(!elem || !name) return 1; sprintf((char*)elem,"Name:%-25sID:%-12d",name,id); elem->Zero3 = '\n'; return 0; } int ReplaceElement(Element *newElem, const char *whereNameIs,FILE *fp) { if(!newElem || !whereNameIs || !fp) return 1; Element oldElem; fseek(fp,3,SEEK_SET); while(fread(&oldElem,sizeof oldElem,1,fp) == 1) { if(memcmp(oldElem.pszName,whereNameIs,sizeof(whereNameIs)) == 0) { fseek(fp, -(sizeof oldElem +1),SEEK_CUR); fwrite(newElem,sizeof *newElem,1,fp); return 0; } } return -1; } int main(int,char** p) { Element elem; FILE *fp = fopen("test.txt","w+"); char bom[] = {0xef,0xbb,0xbf}; fwrite(bom,sizeof bom,1,fp); CreateElement(&elem,"MyName",13); fwrite(&elem,sizeof elem,1,fp); CreateElement(&elem,"UMAD?",13); fwrite(&elem,sizeof elem,1,fp); CreateElement(&elem,"Trololo",13); fwrite(&elem,sizeof elem,1,fp); CreateElement(&elem,"LOL",13); fwrite(&elem,sizeof elem,1,fp); CreateElement(&elem,"New Name",32423); ReplaceElement(&elem,"UMAD?",fp); fclose(fp); return 0;}
glaza Δημοσ. 28 Μαΐου 2011 Μέλος Δημοσ. 28 Μαΐου 2011 Παιδιά σας ευχαριστώ πολύ..@παπί : σε αυτό το code snippet που παράθεσες, στην θέση του New name θα πάει και θα βάλει το UMAD?, έτσι? Οσον αφορα την CreateElement και την ReplaceElement δεν έχω τις απαραίτητες γνώσεις για να τις καταλάβω με την μια αλλά ψιλονιώθω τι κάνουν και θα τις κοιτάξω λεπτομερώς τώρα...Πάντως ισχύουν γενικά για αντικατάσταση σε αρχείο έτσι? Και βασικά μόνο για char ?
παπι Δημοσ. 28 Μαΐου 2011 Δημοσ. 28 Μαΐου 2011 Replace δεν μπορεις να κανεις, μονο write, append και overwrite. Το παραπανω δουλευει με overwrite, δλδ εχεις μια δομη (με στανταρ μεγεθος) που αντιπροσωπευει μια γραμμη στο αρχειο. πχ >typedef struct tagElement { char pszZero1[5]; // space gia "Name:" char pszName[25]; // space gia to onoma char pszZero2[3]; // space gia "ID:" char pszID[12]; // space gia id char Zero3; // space gia \n }Element; // αυτο εδω στην ουσια ειναι ενας πινκας απο χαρακτηρες, απλα το εχω βαλει σε ενα struct για να χωρισω τις μεταβλητες(οναμα,κωδικος) int CreateElement(Element *elem,const char *name, int id) { if(!elem || !name)// ελεγχος return 1; /* sprintf((char*)elem,"Name:%-25sID:%-12d",name,id); elem->Zero3 = '\n'; */ memset(elem,' ',sizeof *elem); //βαζω σε ολο το string κενα αρα εχω αυτο " " strncpy(elem->pszZero1,"Name:",sizeof elem->pszZero1);//βαζω τη πρωτη ετικετα αρα εχω "Name: " strncpy(elem->pszZero2,"ID:",sizeof elem->pszZero2);// βαζω την δευτερη ετικετα "Name: id: " strncpy(elem->pszName,name,sizeof elem->pszName);//τη πρωτη μεταβλητη "Name:XXXXX id: " itoa(id,elem->pszID,10);//την δευτερη μεταβλητη "Name:XXXXXX id:0000 " elem->Zero3 = '\n';// linefeed "Name:XXXXXXX id:0000 \n"; return 0; } Εφοσον εχω φτιαξει την γραμμη, την γραφω οπος ειναι μεσα στο αρχειο με την fwrite
migf1 Δημοσ. 29 Μαΐου 2011 Δημοσ. 29 Μαΐου 2011 Σου παραθέτω και μια άλλη υλοποίηση με χρήση των συναρτήσεων fputs() και fgets() για την εγγραφή και ανάγνωση γραμμών στο και από το αρχείο. Δουλεύει απευθείας με strings (γραμμές) χωρίς να χρησιμοποιεί δομές... > #include <stdio.h> #include <stdlib.h> #include <string.h> // για την strlen() #define MAXSLEN_NAME (25+1) // μέγιστο μήκος ονόματος #define MAXSLEN_ID (3+1) // μέγιστο μήκος κωδικού #define LABEL_NAME "Onoma : " // ετικέτα ονόματος #define MAXSLEN_NAMELBL (8+1) // μέγιστο μήκος ετικέτας ονόματος #define LABEL_ID " Kwdikos : " // ετικέτα κωδικού #define MAXSLEN_IDLBL (11+1) // μέγιστο μήκος ετικέτας κωδικού // μέγιστο μήκος εγγραφής (γραμμής) #define MAXSLEN_LINE (MAXSLEN_NAME + MAXSLEN_ID + MAXSLEN_NAMELBL + MAXSLEN_IDLBL) #define myexit(n) printf("\npress ENTER to exit..."); fflush(stdin); getchar(); exit((n)) // ------------------------------------------------------------------------------------ // Δημιουργία γραμμής στη μνήμη με το όνομα και τον κωδικό των παραμέτρων // char *s_makeline( char *line, const char *name, const char *id ) { if ( !line || !name || !id ) // ανύπαρκτα strings return NULL; // πρόωρος τερματισμός συνάρτησης register int i; // "χτισιμο" της γραμμής strncpy( line, LABEL_NAME, MAXSLEN_NAMELBL ); strncat( line, name, MAXSLEN_NAME+1 ); strncat( line, LABEL_ID, MAXSLEN_IDLBL+1 ); strncat( line, id, MAXSLEN_ID+1 ); // γέμισμα περισσευούμενου χώρου με κενά διαστήματα και ένα '\n' στο τέλος for ( i=strlen(line); i < MAXSLEN_LINE-1; i++ ) line[i] = ' '; line[--i] = '\n'; line[++i] = '\0'; return line; } // ------------------------------------------------------------------------------------ // Αλλαγή της 'pos' γραμμής του αρχείου με νέο όνομα ή/και κωδικό // int f_updline( FILE *fp, const int pos, const char *name, const char *id ) { if ( !fp ) return; char newline[ MAXSLEN_LINE ]; // προσωρινό string γραμμής // τοποθέτηση στην αρχή της 'pos' γραμμής if ( fseek( fp, (pos-1) * MAXSLEN_LINE * sizeof( char ), SEEK_SET ) != 0 ) return 0; s_makeline( newline, name, id); // δημιουργία νέας γραμμής στη μνήμη fputs( newline, fp ); // εγγραφή της στο αρχείο (στη θέση 'pos') return 1; } // ------------------------------------------------------------------------------------ // Εμφάνιση των περιεχομένων του αρχείου 'fp' στην οθόνη // void f_print( FILE *fp ) { if ( !fp ) return; char s[ MAXSLEN_LINE ]; // προσωρινό string fseek( fp, 0, SEEK_SET ); // τοποθέτηση στην αρχή του αρχείου puts("File Contents\n-------------"); while ( fgets(s, MAXSLEN_LINE, fp) ) printf("%s",s); putchar('\n'); return; } // ------------------------------------------------------------------------------------ int main( void ) { char fname[] = "a.txt"; // το όνομα του αρχείου char line[ MAXSLEN_LINE ] = ""; // προσωρινό string μιας γραμμής FILE *fp = fopen( fname, "w+" ); // άνοιγμα του αρχείου if ( !fp ) { // αποτυχία puts("*** fatal error: fopen() failed"); // πρόωρος τερματισμός myexit(1); } fputs( s_makeline( line, "Giorgos", "001" ), fp ); // εγγραφή 3 γραμμών στο αρχείο fputs( s_makeline( line, "Xristos", "002" ), fp ); fputs( s_makeline( line, "Petros", "003" ), fp ); f_print( fp ); // εμφάνιση περιεχομένων αρχείου if ( !f_updline( fp, 3, "Basilhs", "003" ) ) // αλλαγή 3ης γραμμής στο αρχείο printf("*** error: line %d was not updated\n", 3); if ( !f_updline( fp, 1, "Maria", "001" ) ) // αλλαγή 1ης γραμμής στο αρχείο printf("*** error: line %d was not updated\n", 1); f_print( fp ); // εμφάνιση περιεχομένων αρχείου fclose( fp ); // κλείσιμο του αρχείου myexit(0); // τερματισμός του προγράμματος }
glaza Δημοσ. 29 Μαΐου 2011 Μέλος Δημοσ. 29 Μαΐου 2011 Πω θα το σπάσω, δοκιμάζω αυτό που έγραψε ο Παπι και κάνει ότι ναναι..και η πλάκα είναι οτι κι εγώ με δομή το παλεύω ..
migf1 Δημοσ. 30 Μαΐου 2011 Δημοσ. 30 Μαΐου 2011 Είναι πανεύκολο να προσθέσεις δομή στον παραπάνω κώδικα με τα strings, αλλά είναι overkill η μετατροπή της κάθε εγγραφής σε γραμμή. Δες τον παρακάτω κώδικα που τον έγραψα αποκλειστικά για δομές, μόνο που δεν αποθηκεύω ετικέτες στο αρχείο, μόνο κωδικό και όνομα (και μάλιστα με αυτή τη σειρά). Δεν είναι τίποτα να τα προσθέσεις αν θέλεις. Γενικώς δεν συνηθίζεται να αποθηκεύονται οι ετικέτες μέσα στα αρχεία, αυτές μπορείς να τις βάλεις όταν τυπώνεις το αρχείο σου στην οθόνη ή στον εκτυπωτή ( π.χ. στον παρακάτω κώδικα μπορείς να τις προσθέσεις στο printf() της συνάρτησης f_print() ). Δεν ξέρω βέβαια αν σας το επιβάλλει η συγκεκριμένη άσκηση να το κάνετε έτσι. Η s_ncopy() που έχω στον κώδικα παρακάτω είναι μια δική μου παραλλαγή της στάνταρ strncpy(), για να μηδενίζω υποχρεωτικά τον τελικό χαρακτήρα του destination string. Επίσης, δεν κάνω έλεγχο σφαλμάτων όταν αποθηκεύω τις αρχικές εγγραφές στο σώμα της main() για να είναι πιο κατανοητός ο κώδικας. Κάνω όμως λίγο πιο κάτω, όταν αλλάζω την 3η και την 1η εγγραφή του αρχείου, ώστε να υπάρχει σαν δείγμα (κανονικά πρέπει να προστεθεί ο έλεγχος και στις αρχικές εγγραφές). Και τέλος την rec_set() που βάζει τιμές σε μια εγγραφή στη μνήμη την έκανα ξεχωριστή συνάρτηση για να είναι πιο δομημένο το πρόγραμμα (και πιο ευέλικτο σε μελλοντικές προσθήκες). Μπορείς να την ενοποιήσεις με την rec_fwrite() αν θέλεις. Επίσης την rec_fread() δεν την χρησιμοποιώ, την έχω απλά για μελλοντική χρήση. > #include <stdio.h> #include <stdlib.h> #define MAXLEN_ID (3+1) // μέγιστο μήκος κωδικού #define MAXLEN_NAME (25+1) // μέγιστο μήκος ονόματος #define myexit(n) printf("\npress ENTER to exit..."); fflush(stdin); getchar(); exit((n)) typedef enum { FALSE=0, TRUE } bool; typedef struct rec { char id[ MAXLEN_ID ]; char name[ MAXLEN_NAME ]; } Rec; // ------------------------------------------------------------------------------------ char *s_ncopy( char *dst, const char *src, int n ) { char *save = dst; while ( (dst-save) < n-1 && (*dst=*src) != '\0' ) dst++, src++; if ( *dst ) *dst = 0; return save; } // ------------------------------------------------------------------------------------ Rec *rec_set( Rec *rec, const char *id, const char *name ) { if ( !rec ) return NULL; s_ncopy( rec->id, id, MAXLEN_ID ); s_ncopy( rec->name, name, MAXLEN_NAME ); return rec; } // ------------------------------------------------------------------------------------ bool rec_fread( FILE *fp, const int fpos, Rec *rec ) { if ( !fp ) return FALSE; if ( fseek( fp, (fpos-1) * sizeof(Rec), SEEK_SET ) != 0 ) return FALSE; if ( fread( rec, sizeof(Rec), 1, fp ) != 1 ) return FALSE; return TRUE; } // ------------------------------------------------------------------------------------ bool rec_fwrite( FILE *fp, const int fpos, const Rec *newrec ) { if ( !fp ) return FALSE; if ( fseek( fp, (fpos-1) * sizeof(Rec), SEEK_SET ) != 0 ) return FALSE; if ( fwrite( newrec, sizeof(Rec), 1, fp ) != 1 ) return FALSE; return TRUE; } // ------------------------------------------------------------------------------------ bool f_print( FILE *fp ) { if ( !fp ) return FALSE; if ( fseek( fp, 0, SEEK_SET ) != 0 ) // μεταπήδηση στην αρχή του αρχείου return FALSE; Rec rec; // προσωρινή εγγραφή puts("File Contents\n-------------"); while ( fread( &rec, sizeof(Rec), 1, fp ) ) printf( "%s %s\n", rec.id, rec.name ); putchar('\n'); return TRUE; } // ------------------------------------------------------------------------------------ int main( void ) { char fname[] = "b.txt"; // το όνομα του αρχείου Rec rec = { .id="", .name="" }; // προσωρινή εγγραφή FILE *fp = fopen( fname, "w+" ); // άνοιγμα του αρχείου if ( !fp ) { // αποτυχία puts("*** fatal error: fopen() failed"); // προώρος τερματισμός myexit(1); } /* αποθήκευση 3 εγγραφών (χωρίς έλεγχο για σφάλματα) */ rec_set( &rec, "001", "Giorgos" ); // αποθήκευση 1ης εγγραφής rec_fwrite( fp, 1, &rec ); rec_set( &rec, "002", "Xristos" ); // αποθήκευση 2ης εγγραφής rec_fwrite( fp, 2, &rec ); rec_set( &rec, "003", "Petros" ); // αποθήκευση 3ης εγγραφής rec_fwrite( fp, 3, &rec ); f_print( fp ); // εμφάνιση περιεχομένων αρχείου /* αλλαγή εγγραφών 3 και 1 (με έλεγχο για σφάλματα) */ rec_set( &rec, "003", "Basilhs" ); // ΑΛΛΑΓΗ 3ης εγγραφής if ( !rec_fwrite( fp, 3, &rec ) ) puts("\t*** error: write file error"); rec_set( &rec, "001", "Maria" ); // ΑΛΛΑΓΗ 1ης εγγραφής if ( !rec_fwrite( fp, 1, &rec ) ) puts("\t*** error: write file error"); if ( !f_print( fp ) ) // εμφάνιση περιεχομένων αρχείου puts("\t*** error: read file error"); fclose( fp ); // κλείσιμο του αρχείου myexit(0); // τερματισμός του προγράμματος }
Προτεινόμενες αναρτήσεις
Αρχειοθετημένο
Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.