THMMY.gr

Μαθήματα Βασικού Κύκλου => Δομημένος Προγραμματισμός => Topic started by: mamalos on June 19, 2011, 14:30:38 pm



Title: Αρχεία
Post by: mamalos on June 19, 2011, 14:30:38 pm
Μίλησα με κάποιους από εσάς, και κατάλαβα ότι το πρόβλημα με τα αρχεία είναι ότι δε γνωρίζετε τι ακριβώς είναι, οπότε ό,τι σας λέμε ακούγονται λίγο "κινέζικα". Θα αποπειραθώ μία εξήγση ώστε να καταλάβετε τι ακριβώς συμβαίνει με αυτά.

Το αρχείο είναι ένα κομμάτι μνήμης, το οποίο ΕΙΝΑΙ ΣΕΙΡΙΑΚΟ. Αυτό σημαίνει ότι υπάρχει κάτι σαν "βελόνα" (όπως π.χ. η βελόνα του πικάπ, της κασέτας, το λέηζερ του CD/DVD, κ.ο.κ.) η οποία περνάει "πάνω από το αρχείο" και ανάλογα με το τι της λέμε, αυτή διαβάζει ή γράφει πάνω από/σε αυτό. Φυσικά, αυτό που σας λέω είναι μια υπεραπλούστευση του τι συμβαίνει και δεν είναι 100% ακριβές, όμως ως παρομοίωση μπορεί να εκφράσει ακριβώς αυτό που συμβαίνει με τα αρχεία.

Όταν ανοίγουμε, λοιπόν, ένα αρχείο, η βελόνα πηγαίνει στο σημείο που την τοποθετήσαμε με την fopen().  Ακολουθεί κομμάτι του man page της fopen όπως αυτή δίνεται στο FreeBSD, και μπορείτε να το χρησιμοποιείτε ως αναφορά. Μην το διαβάσετε τώρα, θα μπερδευτείτε. Χρησιμοποιήστε το σαν αναφορά όποτε κολλάτε:

    The fopen() function opens the file whose name is the string pointed to
     by path and associates a stream with it.

     The argument mode points to a string beginning with one of the following
     sequences (Additional characters may follow these sequences.):

     “r”     Open text file for reading.  The stream is positioned at the
             beginning of the file.

     “r+”    Open for reading and writing.  The stream is positioned at the
             beginning of the file.

     “w”     Truncate to zero length or create text file for writing.  The
             stream is positioned at the beginning of the file.

     “w+”    Open for reading and writing.  The file is created if it does not
             exist, otherwise it is truncated.  The stream is positioned at
             the beginning of the file.

     “a”     Open for writing.  The file is created if it does not exist.  The
             stream is positioned at the end of the file.  Subsequent writes
             to the file will always end up at the then current end of file,
             irrespective of any intervening fseek(3) or similar.

     “a+”    Open for reading and writing.  The file is created if it does not
             exist.  The stream is positioned at the end of the file.  Subse‐
             quent writes to the file will always end up at the then current
             end of file, irrespective of any intervening fseek(3) or similar.

     The mode string can also include the letter ``b'' either as a third char‐
     acter or as a character between the characters in any of the two-charac‐
     ter strings described above.  This is strictly for compatibility with
     ISO/IEC 9899:1990 (“ISO C90”) and has no effect; the ``b'' is ignored.

Επίσης ας έχουμε στο νου μας τον παρακάτω κώδικα ως παράδειγμα:

1) int a[2], b=15;
2) char c[10] = "lalakis";
3) FILE *fp;
4)
5) fp=fopen("/path/tofile.txt", "r+");
6) fread(&a[0], sizeof(int), 1, fp);
7) a[1]=8;
8) fwrite(&a[1], sizeof(int), 1, fp);
9) fseek(fp, -sizeof(int), "SEEK_CUR");
10) fwrite(&b, sizeof(int), 1, fp);
11) fclose(fp);

Αν, λοιπόν, ανοίξουμε το αρχείο με fopen("/path/tofile.txt", "r"), λέμε στη βελόνα να πάει στην αρχή του αρχείου που βρίσκεται στο δίσκο μας στο μονοπάτι "/path/tofile.txt", και να επιτρέπει ανάγνωση+εγγραφή από/προς αυτό το αρχείο, και να επιστρέψει έναν file pointer στο fp, από όπου εμείς θα μπορούμε να διαχειριστούμε τη βελόνα. Μόλις δώσουμε την εντολή 6), διαβάζουμε έναν (1*sizeof(int) = 4 bytes (συνήθως)) ακέραιο από το αρχείο μας, και το περιεχόμενο που διαβάσαμε με τη βελόνα το γράψαμε στη μνήμη του υπολογιστή που αντιστοιχεί στη θέση &a[0] (:= a για τους περίεργους). Μόλις τελειώσε η fread, η βελόνα βρίσκεται 4 bytes μετά από την αρχή του αρχείου, δηλαδή δείχνει ακριβώς στο 5ο byte.

Η εντολή 8), ζητάει από τη βελόνα να γράψει έναν ακέραιο (1*sizeof(int) = 4bytes, πάλι) από το σημείο που βρίσκεται και μετά. Άρα, εφόσον η βελόνα βρίσκεται στο 5ο byte μετά από την αρχή του αρχείου (αφού δεν ακολούθησε άλλη εντολή που να επηρεάσει τη βελόνα από την εντολή 6 μέχρι την 8), ξεκινάει να γράφει τα 4 bytes ξεκινώντας από το 5ο μέχρι να φτάσει και στο 8ο. Μόλις τελειώσει η εγγραφή, τα δεδομένα έχουν γραφτεί στις προαναφερθείσες θέσεις και η βελόνα βρίσκεται "πάνω" από την 9η θέση (byte) μετά την αρχή του αρχείου.

Αυτό σημαίνει ότι οποιαδήποτε εντολή ανάγνωσης-εγγραφής στο αρχείο (τύπου fread, fwrite, fgets, fputs, fprintf, fscanf, κλπ) εκτελεστεί, θα ξεκινήσει να "ενεργεί" από τη θέση 9 και μετά. Αν θέλουμε να αλλάξουμε κάτι το οποίο προηγείται ή έπεται της θέσης που βρίσκεται η βελόνα, χρησιμοποιούμε εντολές τύπου fseek() για να πάμε τη βελόνα στο σημείο που θέλουμε. Αν, στο παράδειγμά μας, εκτελεστεί η 9η εντολή, τότε η βελόνα μετακινείται 4 bytes (:= sizeof(int)) "αριστερά" (λόγω του "-") από το σημείο που βρίσκεται ήδη η βελόνα (λόγω του "SEEK_CUR"). Άρα η βελόνα πάει στη θέση 9-4=5 και παραμένει εκεί. Όταν, λοιπόν, πάει να εκτελεστεί η εντολή 10), τότε το πρόγραμμα θα γράψει έναν ακέραιο, το 15 (το οποίο βρίσκεται αποθηκευμένο στη μεταβλητή b , το οποίο είνα αποθηκευμένο στη μνήμη "RAM" του υπολογιστή μας) στα 4 bytes του αρχείου που ξεκινάνε από την 5η θέση από την αρχή του. Άρα στις θέσεις 5-8, και η βελόνα μετά την 10η εντολή θα βρίσκεται πάνω από τη θέση 9. Με την συνάρτηση fclose(), κλείνουμε το αρχείο μας.

Η διαφορά, συνεπώς, από αυτά που είχαμε μάθει έως τώρα, είναι ότι στο αρχείο δεν έχουμε "τυχαία" προσπέλαση (εν αντιθέσει με τη RAM, όπου δηλώναμε μεταβλητές, έμπαιναν αυτόματα (από τον compiler) σε κάποια θέση μνήμης όπου μετά βλέπουμε το περιεχόμενό τους χρησιμοποιώντας το όνομα της μεταβλητής που τους δώσαμε). Στο αρχείο, λέμε σε ποιο σημείο θα βρίσκεται η βελόνα τη στιγμή που το ανοίγουμε, λέμε για ποιον λόγο θέλουμε να το χρησιμοποιήσουμε (μέσα από τα "r, w, a" και το +) και ό,τι κίνηση κάνουμε από και προς το αρχείο έχει να κάνει με το που βρίσκεται η βελόνα τη στιγμή που εκτελείται η εντολή. Αναγκαζόμαστε, συνεπώς, να "μετράμε" (υπό κάποια έννοια) τις κινήσεις της βελόνας ώστε να διαβάζουμε/γράφουμε δεδομένα από και προς το αρχείο κατά βούληση.

Δεν ξέρω αν θα μπορώ να σας απαντήσω απορίες σε αυτό το thread (έχω σοβαρό πρόβλημα χρόνου αυτόν τον καιρό, βλέπετε τι μέρα ξεκίνησα το thread...), αλλά θεωρώ ότι σας τα έκανα όσο πιο λιανά μπορούσα. Τώρα μπορείτε να χρησιμοποιήσετε το google για να λύσετε τις απορίες σας ή όποιοι από εσάς έχουν ήδη καταλάβει τι παίζει με τα αρχεία και παρακολουθείτε το thread μπορείτε να λύνετε τις τυχόν απορίες που θα προκύψουν.