Κεφάλαιο 12: Είσοδος και έξοδος δεδομένων σε αρχεία Τα δεδομένα που επεξεργαζόμαστε, καθώς και ο κώδικας που τρέχουμε, βρίσκονται αποθηκευμένα στη μνήμη RAM (Random Access Memory) του υπολογιστή. Τα δεδομένα διατηρούνται εκεί όσο ο υπολογιστής λειτουργεί. Όταν τον σβήσουμε, ό,τι βρίσκεται στη μνήμη RAM χάνεται. Δεδομένα και προγράμματα υπάρχουν αποθηκευμένα και στη μνήμη ROM (Read Only Memory), η οποία, όμως, έχει διαφορετικό σκοπό. Τα περιεχόμενά της τα έχει γράψει ο κατασκευαστής του υπολογιστή και πρόκειται για αποθηκευμένα δεδομένα και προγράμματα τα οποία έχουν να κάνουν με βασικές λειτουργίες και, φυσικά, δεν επιτρέπεται να τροποποιηθούν. Εκεί, οι πληροφορίες διατηρούνται όταν σβήσουμε τον υπολογιστή, αλλά ούτε τις τοποθετήσαμε εμείς, ούτε μπορούμε να τις τροποποιήσουμε. Αν, λοιπόν, επιθυμούμε μια πληροφορία να διατηρηθεί αφότου σβήσουμε τον υπολογιστή, η αποθήκευση πρέπει να γίνει σε κάποια άλλη μονάδα, εσωτερική στον υπολογιστή ή εξωτερική, όπως ο σκληρός δίσκος, ένας οπτικός δίσκος (CD-ROM), μία μνήμη USB (USB stick) ή κάποια κάρτα μνήμης (memory card). Η τεχνολογία τρέχει τόσο πολύ (και) σε αυτό το σημείο, που τα μέσα αποθήκευσης πληθαίνουν μέρα με τη μέρα, αλλά να σημειώσουμε ότι γίνονται όλο και πιο γρήγορα και πιο αξιόπιστα. Τα μέσα αποθήκευσης εξελίσσονται τόσο γρήγορα, που σε πολύ λίγο καιρό αυτή η παράγραφος θα χρειάζεται ενημέρωση. Τα αρχεία (files) αποτελούν δομημένα βασικά στοιχεία πληροφορίας τα οποία βρίσκονται στα μέσα αποθήκευσης μιας υπολογιστικής μονάδας. Η μορφή τους ποικίλλει ανάλογα με τον τύπο τους και θα μπορούσε να είναι: δεδομένα, κείμενο (text), εικόνα (image), ήχος (sound), video ή κάποια άλλη πολυμεσική 184
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 185 πληροφορία (multimedia). Ακόμα και ένα πρόγραμμα το οποίο θα αποθηκεύσουμε στον δίσκο θεωρείται ένα αρχείο, είτε είναι σε μορφή πηγαίου κώδικα είτε είναι μεταφρασμένο σε γλώσσα μηχανής. Όλα αυτά θα μείνουν στο μέσο αποθήκευσης και αφού σβήσει ο υπολογιστής, και θα παραμείνουν εκεί μέχρι να τα ζητήσουμε ξανά. Δεν είναι, όμως, μόνο αυτός ο λόγος για τον οποίο γράφουμε πληροφορία στα αρχεία. Μέσα από αυτά μπορούμε να τη μοιραστούμε με άλλους, να τη στείλουμε σε διαφορετικούς υπολογιστές ή και σε άλλες συσκευές που έχουν τη δυνατότητα και τη νοημοσύνη να την καταλάβουν, ή απλά να την παρουσιάσουν στον χρήστη τους. Η Python, όπως και όλες οι γλώσσες προγραμματισμού για τις οποίες αυτό έχει νόημα, υποστηρίζει αρχεία. Τα περισσότερα από αυτά που θα δούμε ακολουθούν τη φιλοσοφία που θα συναντούσε κανείς στις περισσότερες γλώσσες προγραμματισμού. 12.1 Άνοιγμα και κλείσιμο αρχείου Είναι το πρώτο και το τελευταίο πράγμα που κάνουμε με ένα αρχείο. Λέγοντας άνοιγμα (open) εννοούμε ότι πληροφορούμε την Python ότι θέλουμε να χρησιμοποιήσουμε το συγκεκριμένο αρχείο. Εκείνη το βρίσκει στο μέσο αποθήκευσης και το κάνει διαθέσιμο σε εμάς. Κάνοντας κλείσιμο (close) ενός αρχείου πληροφορούμε την Python ότι το αρχείο δεν μας χρειάζεται άλλο, έχουμε διαβάσει ή τροποποιήσει ό,τι χρειαζόμασταν, και μπορεί να θεωρήσει ότι δεν το θέλουμε πια να είναι ανοικτό. Το άνοιγμα ενός αρχείου γίνεται με την κλήση της open. Η open παίρνει σαν παράμετρο το όνομα ή την πλήρη διαδρομή του αρχείου, πώς, δηλαδή, αυτό ονομάζεται και πού βρίσκεται στο μέσο αποθήκευσης. Στη δεύτερη παράμετρο δηλώνουμε τον τρόπο με τον οποίο θέλουμε να ανοίξουμε το αρχείο και οι επιλογές μας είναι οι εξής: r (άνοιγμα για ανάγνωση -read): Επιθυμούμε μόνο να διαβάσουμε πληροφορία από το αρχείο w (άνοιγμα για εγγραφή - write): Επιθυμούμε να γράψουμε πληροφορία στο αρχείο. Ό,τι πληροφορία υπήρχε στο αρχείο πριν το ανοίξουμε χάνεται. Αν το όνομα του αρχείου που δώσαμε δεν υπάρχει στο μέσο αποθήκευσης, τότε θα δημιουργηθεί ένα καινούργιο αρχείο. a (άνοιγμα για πρόσθεση - append): Επιθυμούμε να γράψουμε
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 186 πληροφορία στο αρχείο χωρίς να χαθεί η πληροφορία που υπήρχε εκεί πριν το ανοίξουμε. Η νέα πληροφορία θα γραφεί μετά την παλιά. Αν το αρχείο δεν υπάρχει στο μέσο αποθήκευσης, τότε θα δημιουργηθεί ένα νέο. Η κλήση open επιστρέφει σαν αποτέλεσμα έναν δείκτη στο αρχείο. Εμάς (σε αυτό τουλάχιστον το βιβλίο) δεν μας νοιάζει ακριβώς τι είναι αυτό, αλλά πώς θα το χρησιμοποιήσουμε. Ο δείκτης αυτός θα χρησιμοποιηθεί από εδώ και πέρα όποτε θέλουμε να προσπελάσουμε το αρχείο, για μια πράξη ανάγνωσης ή εγγραφής, για παράδειγμα, ή για να κλείσουμε ένα αρχείο. Για να κλείσουμε το αρχείο, απλά καλούμε την close εφαρμόζοντάς την στον δείκτη του αρχείου. Η σύνταξη της open είναι η ακόλουθη: fp = open(file_name,mode) fp: ο δείκτης που θα δημιουργηθεί για το αρχείο, file_name: το όνομα ή η πλήρης διαδρομή για το αρχείο, και mode: το αν θα το ανοίξουμε για διάβασμα, για γράψιμο ή για πρόσθεση Η σύνταξη της close είναι η ακόλουθη: fp.close() fp: ο δείκτης στο αρχείο. 12.2 Λειτουργίες εγγραφής Η εγγραφή σε ένα αρχείο γίνεται με την write. Η write εφαρμόζεται στον δείκτη του αρχείου και παίρνει σαν παράμετρο μια συμβολοσειρά, την οποία και γράφει στο αρχείο. Η σύνταξή της ακολουθεί: fp.write(s) fp: ο δείκτης στο αρχείο, και s: η συμβολοσειρά που θα γραφεί στο αρχείο. Αν έχουμε μία λίστα από συμβολοσειρές, τότε μπορούμε να χρησιμοποιήσουμε την writelines η οποία θα γράψει μία μία όλες τις συμβολοσειρές της
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 187 λίστας στο αρχείο, με τη σειρά που αυτές συναντώνται στη λίστα. Η σύνταξή της ακολουθεί: fp.writelines(l) fp: ο δείκτης στο αρχείο, και L: η λίστα με τις συμβολοσειρές που θα γραφούν στο αρχείο Σχήμα 12.1: Παράδειγμα με εγγραφές σε ένα αρχείο. Στο Σχήμα 12.1 φαίνεται ένα παράδειγμα στο οποίο κάνουμε κάποιες εγγραφές σε ένα αρχείο. Έστω ότι στις μεταβλητές s και t έχουμε εκχωρημένες τις τιμές s= This is a test file\n και t= to test python files\n οι οποίες και θέλουμε να αποτελέσουν τις δύο πρώτες γραμμές του αρχείου με το όνομα testfile. Στην αρχή ανοίγουμε το αρχείο testfile για εγγραφή ( w ) με την open. Μετά καλούμε την f.write() δύο φορές, τη μία με παράμετρο s για να γράψει στην πρώτη γραμμή το This is a test file\n και τη δεύτερη με την παράμετρο t για να γράψει τη γραμμή to test python files\n. Προσέξτε τα
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 188 σύμβολα \n στο τέλος κάθε συμβολοσειράς, που υποδηλώνουν αλλαγή γραμμής. Αν δεν υπήρχαν, οι δύο φράσεις θα ήταν η μία δίπλα στην άλλη στην ίδια γραμμή του αρχείου. Στη συνέχεια κλείνουμε το αρχείο εφαρμόζοντας την close πάνω στην f, δηλαδή γράφοντας f.close(). Ας υποθέσουμε τώρα ότι μια άλλη μέρα θέλουμε να συνεχίσουμε να γράφουμε παρακάτω στο ίδιο αρχείο. Έστω ότι οι δύο γραμμές που θέλουμε να γράψουμε βρίσκονται στη λίστα lst. Θα ανοίξουμε το ίδιο αρχείο, το testfile, για πρόσθεση ( a ) πάλι με την open. Αυτήν τη φορά, όμως, θα καλέσουμε τη writelines, μια που όλα είναι έτοιμα μέσα στη λίστα lst. Αφού κλείσουμε το αρχείο, μπορούμε με έναν εκδότη κειμένου να δούμε τι περιέχει μέσα. Θα πάρουμε την εικόνα του Σχήματος 12.2. Σχήμα 12.2: Το αρχείο που φτιάξαμε σε έναν εκδότη κειμένου. 12.3 Λειτουργίες ανάγνωσης Οι κυριότερες λειτουργίες ανάγνωσης στην Python είναι οι read, readline και readlines. Καθεμίας η σύνταξη και περιγραφή ακολουθούν: H read διαβάζει Ν χαρακτήρες από το αρχείο. Αν το Ν παραλειφθεί σαν παράμετρος, τότε διαβάζει όλο το αρχείο. Η σύνταξή της είναι η ακόλουθη: fp.read(ν) fp: ο δείκτης στο αρχείο, και Ν: ο αριθμός των χαρακτήρων που θα διαβαστούν H readine διαβάζει μία γραμμή από το αρχείο. Η σύνταξή της είναι η ακόλουθη:
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 189 fp.readline() fp: ο δείκτης στο αρχείο H readines διαβάζει όλο το αρχείο και το τοποθετεί γραμμή γραμμή σε μία λίστα. Η σύνταξή της είναι η ακόλουθη: fp.readlines() fp: ο δείκτης στο αρχείο. Σχήμα 12.3: Παράδειγμα αναγνώσεων από ένα αρχείο. Στο Σχήμα 12.3 χρησιμοποιούμε τις παραπάνω λειτουργίες για να διαβάσουμε την πληροφορία που γράψαμε στο αρχείο testfile προηγουμένως. Αρχικά, δοκιμάζουμε την read. Ανοίγουμε το αρχείο και καλούμε την print με παράμετρο την f.read(). H read θα διαβάσει όλο το αρχείο f, δηλαδή το testfile, και η print θα το τυπώσει στην οθόνη. Στο δεύτερο παράδειγμα στο ίδιο σχήμα εφαρμόζουμε πρώτα τη read στο f με παράμετρο 6. Με αυτόν τον τρόπο θα τυπωθούν οι 6 πρώτοι χαρακτήρες του αρχείου, δηλαδή το This i
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 190 Όταν στη συνέχεια καλέσουμε την read χωρίς παράμετρο, θα διαβαστεί και θα τυπωθεί ολόκληρο το υπόλοιπο αρχείο, δηλαδή από τον χαρακτήρα s και κάτω. Συνολικά θα δούμε τυπωμένο το ακόλουθο: This i s a test file to test python files This is another test file to test python files Στο τρίτο παράδειγμα χρησιμοποιούμε τη readline. Αυτή αναμένουμε να διαβάσει μόνο μία γραμμή. Καλώντας την τυπώνεται μόνο η πρώτη γραμμή του αρχείου, δηλαδή: This is a test file Στο τελευταίο παράδειγμα δοκιμάζουμε τη readlines. Καλώντας τη διαβάζεται, γράφεται σε μία λίστα όλο το αρχείο και στη συνέχεια τυπώνεται η λίστα στην οθόνη μέσα από την print. Το αποτέλεσμα που θα εμφανιστεί στην οθόνη είναι το: [ This is a test file\n, to test python files\n, This is another test file\n, to test python files\n ] Το σύμβολο \n υποδηλώνει το τέλος μιας γραμμής κειμένου. 12.4 Λειτουργίες επανάληψης σε αρχεία Ένα από τα σημεία της Python που παρουσιάζει ιδιαίτερο ενδιαφέρον είναι ο τρόπος που υποστηρίζει κάποιες δομές επανάληψης. Θυμίζουμε λίγο τη δομή for, η οποία μπορεί να διαπεράσει με λίγο κώδικα όλα τα στοιχεία μιας λίστας. Στο Σχήμα 12.4 φαίνεται ένα παράδειγμα στο οποίο διαπερνάται η λίστα L και τυπώνονται όλα τα στοιχεία της που διαιρούνται ακριβώς με το 5. Σχήμα 12.4: Παράδειγμα διαπέρασης λίστας με τη for. Ανάλογες πολύ βολικές δομές υπάρχουν και για τις λειτουργίες στα αρχεία. Στο Σχήμα 12.5 φαίνονται δύο παραδείγματα επανάληψης με τη read.
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 191 Στο πρώτο διαβάζονται ένας ένας οι χαρακτήρες του αρχείου. Ο πρώτος έξω από τον βρόχο, οι υπόλοιποι μέσα. Κάθε χαρακτήρας τυπώνεται με την print, η οποία έχει στο τέλος της το end= ώστε να μην αλλάζει γραμμή αφού τυπωθεί ο χαρακτήρας. Ο βρόχος του while σταματάει να εκτελείται μόλις το char πάρει την τιμή eof, στο τέλος αρχείου δηλαδή. Μπορείτε να το διαβάσετε και έτσι: όσο αυτό που διαβάζουμε είναι χαρακτήρας, μένουμε μέσα στον βρόχο. Μόλις διαβαστεί κάτι άλλο (το eof στην περίπτωσή μας), βγαίνουμε από τον βρόχο. Σχήμα 12.5: Παράδειγμα δομής επανάληψης με while για διάβασμα από αρχείο. Το ίδιο ακριβώς κάνει και το επόμενο παράδειγμα και με την ίδια λογική, μόνο που χρησιμοποιεί τη readline. Εδώ το end= το χρησιμοποιούμε διότι στο τέλος κάθε γραμμής του αρχείου υπάρχει ένας χαρακτήρας αλλαγής γραμμής. Επειδή το print θα προκαλούσε μία αλλαγή γραμμής ακόμα, χρησιμοποιούμε το end= για να μη συμβεί αυτό. Στο Σχήμα 12.6 φαίνονται τα ίδια παραδείγματα, αλλά με τη χρήση της for. Παρατηρήστε πόσο πιο όμορφο είναι. Είναι σαν να μιλάμε: Για κάθε χαρακτήρα στο αρχείο (ή για κάθε γραμμή στο αρχείο) κάνε τα εξής. Στο τελευταίο από αυτά, μάλιστα, δεν χρησιμοποιούμε καθόλου τις κλήσεις read και readlines, αντίθετα έχουμε το πιο φιλικό: for line in f:
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 192 Σχήμα 12.6: Παράδειγμα δομής επανάληψης με for για διάβασμα από αρχείο. 12.5 Εγγραφή και ανάγνωση ολόκληρων δομών σε δυαδικά αρχεία Τέλος, θα δούμε πώς είναι δυνατόν να γράψουμε μια ολόκληρη δομή σε ένα αποθηκευτικό μέσο. Θα δούμε εδώ και μία άλλη μορφή αρχείων, τα δυαδικά. Είναι λίγο καταχρηστική η διάκριση σε δυαδικά και μη αρχεία, αφού όλα τα αρχεία τελικά εγγράφονται σε δυαδική μορφή στο αποθηκευτικό μέσο. Αυτό που θέλουμε να δείξουμε εδώ και να διαφοροποιήσουμε από τα αρχεία κειμένου είναι ότι η κωδικοποίηση της πληροφορίας εδώ είναι διαφορετική και δεν είναι δυνατόν να δούμε το περιεχόμενο ενός αρχείου απλά με έναν εκδότη κειμένου. Για τα αρχεία αυτής της μορφής θα χρησιμοποιήσουμε μία βιβλιοθήκη, την pickle. Στο Σχήμα 12.7 φαίνεται το παράδειγμα στο οποίο θα βασιστούμε. Θα γράψουμε ένα αρχείο το οποίο θα έχει μέσα του δύο λίστες, τις οποίες και στη συνέχεια θα διαβάσουμε. Οι λίστες βρίσκονται στις μεταβλητές x και y. Ανοίγουμε ένα νέο αρχείο, έστω το testfile, για εγγραφή. Παρατηρήστε όμως ότι η δεύτερη παράμετρος της open είναι wb και όχι w, όπως αναμενόταν. Αυτό γίνεται για να δηλώσουμε ότι το αρχείο είναι σε δυαδική μορφή. Στη συνέχεια καλούμε την pickle.dump δύο φορές. Τη μία για να γράψουμε τη λίστα x και την άλλη για την y. Είναι απλό. Χωρίς να ανησυχούμε, η Python θα κωδικοποιήσει και θα τοποθετήσει τις δύο λίστες μέσα στο αρχείο. Κλεί-
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 193 νουμε το αρχείο και τελειώσαμε με τις εγγραφές. Αν επιχειρήσουμε να ανοίξουμε το αρχείο με έναν εκδότη κειμένου, θα δούμε κάτι ακαταλαβίστικα σύμβολα στην οθόνη, οπότε μην το κάνετε. Η διαδικασία ανάγνωσης είναι το ίδιο απλή και φαίνεται στη συνέχεια στο ίδιο σχήμα. Θα ανοίξουμε το αρχείο, πάλι χρησιμοποιώντας το b, δηλαδή η δεύτερη παράμετρος της open θα είναι τώρα rb. Στη συνέχεια θα καλέσουμε την pickle.load δύο φορές, με την πρώτη θα διαβαστεί η πρώτη λίστα που αποθηκεύτηκε και με τη δεύτερη η δεύτερη. Τόσο απλό. Κλείνουμε το αρχείο και τελειώσαμε. Σχήμα 12.7: Εγγραφή και ανάγνωση σε δυαδικά αρχεία. Περισσότερο υλικό σχετικό με αναγνώσεις και εγγραφές σε αρχεία μπορείτε να διαβάσετε στο κεφάλαιο 8 του βιβλίου [1], στο κεφάλαιο 14 του βιβλίου [2] και στο κεφάλαιο 13 του βιβλίου [3]. Βιβλιογραφία 1. Jennifer Campel, Paul Gries, Jason Montojo, Greg Wilson (2019). Practical Programming, An Introduction to Computer Science Using Python. Publisher: The Pragmatic Bookself. 2. Allen B. Downey (2012). Think Python. Publisher: O Reilly Media.
ΚΕΦΑΛΑΙΟ 12. ΕΙΣΟΔΟΣ ΚΑΙ ΕΞΟΔΟΣ ΔΕΔΟΜΕΝΩΝ ΣΕ ΑΡΧΕΙΑ 194 3. Brian Heinold (2012). Introduction to Programming Using Python. Publisher: Mount St. Mary s University, Ηλεκτρονικό βιβλίο, ελεύθερα διαθέσιμο.