Προς το περιεχόμενο

Εγγραφή και ανάγνωση αρχείου txt στη C


Giorgos3924

Προτεινόμενες αναρτήσεις

Δημοσ.

Για την δυναμική διαχείρισή μνήμης βρήκα ένα πρόγραμμα.

Το παρακάτω πρόγραμμα δεσμεύει 10bytes μνήμης. Μετά ζητάει να πληκτρολογηθεί ένα σύνολο χαρακτήρων. Τέλος, εμφανίζει στην οθόνη το σύνολο χαρακτήρων και μετά απελευθερώνει τις θέσεις που δέσμευσε.

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

main()
{
char *k;
k=(char *)malloc(10);
if(k==NULL) 
{
	puts("Den yparxei arketi mnimi gia desmeysi");
	exit(2);
}
gets(k);	
puts(k);
free(k);
system("pause");
}

Απ' ότι ξέρω, ένας χαρακτήρας καταλαμβάνει 1 byte στη μνήμη.

Άρα εδώ μπορούμε να καταχωρήσουμε στον δείκτη k μέχρι 10 χαρακτήρες.

Το πρόγραμμα όμως μπορεί να δεχτεί και να τυπώσει περισσότερους από 10 χαρακτήρες.

Γιατί συμβαίνει αυτό; Μήπως κατάλαβα κάτι λάθος;

 

Εγώ χρησιμοποιώ το Dev-C++ Πώς μπορώ να δώ πόσα byte δεσμεύει το πρόγραμμά μου για τις λειτουργίες που εκτελεί;

  • Απαντ. 40
  • Δημ.
  • Τελ. απάντηση
Δημοσ.

Ο παραπάνω κώδικας είναι λανθασμένος, από την άποψη ότι δεν ελέγχει αν η είσοδος που θα δοθεί από τον χρήστη χωρά στο δυναμικό k array. Η σωστή προσέγγιση θα ήταν η χρήση της fgets, η οποία παίρνει όρισμα για το πόσους χαρακτήρες το πολύ θα διαβάσει από την είσοδο.

Δημοσ.

Διαβάζει γενικά από εκεί που θα της υποδείξεις μέσω του τρίτου ορίσματός της (τύπου FILE*). Το οποίο μπορεί κάλλιστα να είναι και το standard input (stdin), δηλαδή το πληκτρολόγιο στις περισσότερες των περιπτώσεων.

 

Ρίξε μια ματιά στο documentation της, π.χ.: http://www.cplusplus.com/reference/clibrary/cstdio/fgets/

Δημοσ.

Ναι η fgets όμως ζητάει όρισμα για το πλήθος των χαρακτήρων που θα διαβάσει.

Είναι λογικό έτσι να μην εμφανίζει περισσότερα απ όσα της λες να διαβάσει μετά.

Απλά προσπαθώ να καταλάβω την προηγούμενη λογική. Εφόσον δεσμέυω πχ 10 θέσεις για έναν πίνακα. και στη συνέχεια διαβάζω 20. Όταν εκτυπώνω τον πίνακα πως γίνεται να εκτυπώνει 20 χαρακτήρες; :confused:

Μήπως κοιτάει το πλήθος των χαρακτήρων και στη συνέχεια δεσμεύει τόσες θέσεις το πρόγραμμα;

Αλλά στη malloc λέει ξεκάθαρα ότι δεσμεύουμε 10 bytes.

Δημοσ.
Για την δυναμική διαχείρισή μνήμης βρήκα ένα πρόγραμμα.

Το παρακάτω πρόγραμμα δεσμεύει 10bytes μνήμης. Μετά ζητάει να πληκτρολογηθεί ένα σύνολο χαρακτήρων. Τέλος, εμφανίζει στην οθόνη το σύνολο χαρακτήρων και μετά απελευθερώνει τις θέσεις που δέσμευσε.

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

main()
{
char *k;
k=(char *)malloc(10);
if(k==NULL) 
{
	puts("Den yparxei arketi mnimi gia desmeysi");
	exit(2);
}
gets(k);	
puts(k);
free(k);
system("pause");
}

Απ' ότι ξέρω, ένας χαρακτήρας καταλαμβάνει 1 byte στη μνήμη.

Άρα εδώ μπορούμε να καταχωρήσουμε στον δείκτη k μέχρι 10 χαρακτήρες.

Το πρόγραμμα όμως μπορεί να δεχτεί και να τυπώσει περισσότερους από 10 χαρακτήρες.

Γιατί συμβαίνει αυτό; Μήπως κατάλαβα κάτι λάθος;

 

Εγώ χρησιμοποιώ το Dev-C++ Πώς μπορώ να δώ πόσα byte δεσμεύει το πρόγραμμά μου για τις λειτουργίες που εκτελεί;

 

Καλο ειναι εφοσον εχεις δεσμευση μνημη για 10 να μην χρησιμοποιεις παραπανω.

 

Το προγραμμα δε σκαει επειδη στη πραγματικοτητα δεν εχεις δεσμευσει 10 byte, αλλα ζητας απο το tlb να σου δεσμευσει 10 byte η οποια δεσμευει την φυσικη μνημη σε blocks.

Ενα οχι και τοσο πετυχημενο παραδειγμα για να παρεις το μεγεθος του μπλοκ ειναι το παρακατω

>#include <stdlib.h>
typedef unsigned int ptr;
int main(int argc, char** argv)
{
ptr f =  (ptr)malloc(1);
ptr s =  (ptr)malloc(1);
printf("Block size:%d",s-f);
return 0;
}

 

Αλλα καλυτερα να μην προκαλεις overflow γιατι θα εχεις περιεργα προβληματα

 

πχ

>#include <stdlib.h>
typedef unsigned int ptr;
int main(int argc, char** argv)
{
char* f =  (char*)malloc(124);
char* s =  (char*)malloc(124);
memcpy(f+124+60,"lol",4);
printf(s);
getchar();
return 0;
}

 

Και το χειροτερο απ'ολα, μπορεις να σπασει τον heapstruct(ο οποιος βρισκετε πισω απο τον δεικτη) του επομενου δεικτη. Αν το κανεις αυτο θα κρασαρει το προγραμμα στο καλεσμα της free

πχ

>#include <stdlib.h>
typedef unsigned int ptr;
int main(int argc, char** argv)
{
char* f =  (char*)malloc(124);
char* s =  (char*)malloc(124);
memcpy(f+124+50,"Paei o heap struct huhuhu 100% tha faw crash",30);
free(s);
getchar();
return 0;
}

Δημοσ.

>
k=(char *)malloc(10);

 

Αν το πρόγραμμα γίνει compile με έναν C compiler (και όχι με C++ compiler)

τότε δεν χρειάζεται το cast (char *) και καλό είναι να αποφεύγεται.

 

>
gets(k);

 

Ό,τι είπε ο parsifal. Χρησιμοποίησε fgets αντί για gets.

 

>

system("pause");
}

Η system("pause") συναντιέται πάρα πολύ συχνά σε προγράμματα που έχουν

γραφτεί για Windows ώστε να βλέπεις την έξοδο των προγραμμάτων. Αντί

αυτής μπορεί να χρησιμοποιεί η getchar ώστε το πρόγραμμα να παίζει παντού.

 

Απ' ότι ξέρω, ένας χαρακτήρας καταλαμβάνει 1 byte στη μνήμη.

 

Αν μιλάμε για μια μεταβλητή τύπου char, τότε έχεις δίκιο. Καταλαμβάνει 1 byte.

 

 

Θεωρητικά καταλαμβάνει ένα C byte. Στην C, byte θεωρείται ο χώρος που

χρειάζεται για να αποθηκεύσουμε ένα χαρακτήρα. Αυτό το C byte μπορεί να μην

έχει 8 bits μέγεθος. Πρακτικά όμως όταν μιλάμε για τους x86/x86_64 επεξεργαστές

που έχουμε όλοι, τότε byte == C Byte == 8 bits

 

 

Ένας σταθερός χαρακτήρας (char literal) π.χ '5', καταλαμβάνει όσο ένα int στην C

και όσο ένα char στην C++. Τέλος μια μεταβλητή τύπου wchar_t συνήθως καταλαμβάνει

4 bytes.

Δημοσ.
Ναι η fgets όμως ζητάει όρισμα για το πλήθος των χαρακτήρων που θα διαβάσει.

Είναι λογικό έτσι να μην εμφανίζει περισσότερα απ όσα της λες να διαβάσει μετά.

Απλά προσπαθώ να καταλάβω την προηγούμενη λογική. Εφόσον δεσμέυω πχ 10 θέσεις για έναν πίνακα. και στη συνέχεια διαβάζω 20. Όταν εκτυπώνω τον πίνακα πως γίνεται να εκτυπώνει 20 χαρακτήρες; :confused:

Μήπως κοιτάει το πλήθος των χαρακτήρων και στη συνέχεια δεσμεύει τόσες θέσεις το πρόγραμμα;

Αλλά στη malloc λέει ξεκάθαρα ότι δεσμεύουμε 10 bytes.

 

Όπως είπε και ο Evgenios1, ο κώδικάς σου δεσμεύει μεν 10 bytes για τον k, αλλά τα επιπλέον bytes γράφονται σε χώρο μνήμης που δεν ξέρεις τί, γιατί και πώς μπορεί να χρησιμοποιούνται. Αναλόγως του compiler και του περιβάλλοντος εκτέλεσης, μπορεί να έχεις από φαινομενικά φυσιολογική συμπεριφορά χωρίς crashes (όπως στην περίπτωσή σου) μέχρι segmentation faults και, το χειρότερο (ναι, χειρότερο και από segfaults): σιωπηρή αλλοίωση των τιμών άλλων μεταβλητών και structs που χρησιμοποιείς στο πρόγραμμά σου και άντε βρες μετά γιατί το πρόγραμμα κάνει τα δικά του.

 

Bottom line: Don't do it! Η C δε σε προστατεύει από τέτοια πράγματα και μία μικρή απροσεξία στον κώδικα μπορείς άνετα να σημαίνει ότι μόλις πυροβόλησες τον εαυτό στο στο πόδι. Κώδικας σχετικός με χειροκίνητο memory allocation και deallocation πρέπει να γράφεται με φειδώ και σούπερ προσοχή.

  • 4 εβδομάδες αργότερα...
Δημοσ.

Καλημέρα!

Έχω ένα πρόβλημα στην εγγραφή και ανάγνωση binary αρχείων.

Στο παρακάτω πρόγραμμα:

>int main()
{
   FILE *fp1,*fp2;
   char grammata[200];
   fp1=fopen("paradeigma.txt","rb");
   fseek(fp1,50,0); [color="RoyalBlue"]//Ξεκινώντας από το 50ο byte του αρχείου paradeigma.txt[/color]
   fread(grammata,10,1,fp1); [color="RoyalBlue"]//Διαβάζουμε τα επόμενα 10 byte[/color]
   fclose(fp1);
   fp2=fopen("test.txt","wb"); 
   fseek(fp2,50,0); [color="RoyalBlue"]//Από το 50ο byte του αρχείου test.txt[/color]
   fwrite(grammata,1,10,fp2); [color="RoyalBlue"]//Ξεκινάμε και γράφουμε τα πρώτα 10 byte του πίνακα grammata[/color]
   fclose(fp2);
   system("PAUSE");
}

Έστω ότι έχω διάφορους τυχαίους χαρακτήρες και στα 2 αρχεία.

Από το ένα αρχείο διαβάζω κάποια byte χωρίς κανένα πρόβλημα.

Κατά την εγγραφή των byte που διαβάστηκαν, στο αρχείο test.txt γράφονται οι

10 χαρακτήρες αντικαθιστώντας όμως όλους τους προηγούμενους με κενό.

Δηλαδή υπάρχει κενό 50 bytes και 10 χαρακτήρες στο τέλος.

Δεν έπρεπε να κρατάει αυτούς τους χαρακτήρες; Γιατί σβήνονται;

Δημοσ.
Δεν έπρεπε να κρατάει αυτούς τους χαρακτήρες; Γιατί σβήνονται;

 

Το ανοίγεις σε w(rite) mode:

 

>fp2=fopen("test.txt","wb");

 

...αλλά σύμφωνα με τα υπόλοιπα που γράφεις στο μήνυμά σου, θα έπρεπε να το ανοίγεις σε a(ppend) mode για να έχεις τη συμπεριφορά που ζητάς (χμμμ, κι αυτό κανονικά υπό συζήτηση είναι, εξαρτάται από το τί μέγεθος έχει το αρχείο που ανοίγεις σε σχέση με το offset από το οποίο θέλεις να αρχίσεις να γράφεις).

 

 

fopen [email protected]:

 

"w": Create an empty file for writing. If a file with the same name already exists its content is erased and the file is treated as a new empty file.

"a": Append to a file. Writing operations append data at the end of the file. The file is created if it does not exist.

Δημοσ.

Όπως είπε ο Parsifal επειδή το ανοίγεις σε write κατάσταση, το αρχείο γίνεται

truncate ό,τι είχε δηλαδή χάνεται. Για να μην χάνονται τα δεδομένα έχεις δύο μεθόδους:

 

1)

Η κατάσταση append που ανέφερε ο parsifal.

>
fp2=fopen("test.txt","ab");

 

Στις περισσότερες libc (τουλάχιστον σε Linux,Free/Net/OpenBSD) οι εγγραφές

θα γίνονται στο τέλος ανεξαρτήτως αν έχεις εκτελέσει κάποια fseek πρώτα.

Δηλαδή δεν θα γράφει στο byte 50 που θέλεις αλλά στο εκάστοτε τέλος του αρχείου.

 

2)

>
fp2=fopen("test.txt","r+b");

Έτσι επιλέγεις ανάγνωση αλλά και εγγραφή. Η θέση εγγραφής αρχικά βρίσκεται

στην αρχή του αρχείου. Σε αυτή την κατάσταση μπορείς να γράψεις σε όποιο

σημείο θέλεις και όχι μόνο στο τέλος.

 

Υπάρχει όμως ένα πρόβλημα. Το πρότυπο ANSI της C ορίζει πριν από κάθε

εναλλαγή μεταξύ ανάγνωσης και εγγραφής πρέπει να υπάρχει μια εντολή

αλλαγή θέσης. Δηλαδή δεν μπορείς να κάνεις μια fwrite και αμέσως μετά μια fread.

 

Η υλοποίηση της glibc του Linux και της libc του FreeBSD έχουν σπάσει το πρότυπο

και επιτρέπουν την εναλλαγή. Τα Net/OpenBSD τηρούν το πρότυπο οπότε πρέπει

να υπάρχει μια fseek έστω και αν δεν κάνει τίποτα, π.χ:

>
fseek(fp2, 0, SEEK_CUR);

 

Ανάλογα τι λειτουργικό χρησιμοποιείς και κατά πόσο θέλεις να είναι portable

το πρόγραμμά σου ή όχι, ακολούθησε την αντίστοιχη πρακτική.

 

Επίσης το "b" είχε νόημα στις εποχές της turbo c σε DOS. Σε όλα τα UNIXοειδή

λειτουργικά, είτε το βάλεις είτε όχι δεν παίζει ρόλο.

 

Edit: Αν κρίνω από το system(PAUSE) χρησιμοποιείς Windows. Βρήκα αυτό

το url της MS http://msdn.microsoft.com/en-us/library/yeby3zcb%28VS.80%29.aspx

που αναφέρει τα εξής:

When a file is opened with the "a" or "a+" access type, all write operations occur at the end of the file. The file pointer can be repositioned using fseek or rewind,

but is always moved back to the end of the file before any write operation is carried out. Thus, existing data cannot be overwritten.

 

When the "r+", "w+", or "a+" access type is specified, both reading and writing are allowed (the file is said to be open for "update"). However, when you switch

between reading and writing, there must be an intervening fflush, fsetpos, fseek, or rewind operation.

 

Οπότε και σε Windows ισχύουν όλα όσα είπα πριν εκτός από

το θέμα του "b". Για binary κατάσταση χρειάζεται το "b".

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...