Αρχεία

Μονιμότητα

Μέχρι στιγμής, μάθαμε πώς να γράφουμε προγράμματα και να επικοινωνούμε τις προθέσεις μας στην Κεντρική Μονάδα Επεξεργασίας χρησιμοποιώντας δομή επιλογής, συναρτήσεις και δομή επανάληψης. Μάθαμε πώς να δημιουργούμε και να χρησιμοποιούμε δομές δεδομένων στην Κύρια μνήμη. Η CPU και η μνήμη είναι εκεί όπου λειτουργεί και εκτελείται το λογισμικό μας. Εκεί συμβαίνει όλη η «σκέψη».

Αλλά αν θυμάστε από τις συζητήσεις μας για την αρχιτεκτονική υλικού, μόλις απενεργοποιηθεί η τροφοδοσία, ό,τι είναι αποθηκευμένο είτε στην CPU είτε στην κύρια μνήμη διαγράφεται. Έτσι, μέχρι τώρα, τα προγράμματά μας ήταν απλώς παροδική διασκέδαση, ασκήσεις για την εκμάθηση Python.

Δευτερεύουσα Μνήμη
Δευτερεύουσα Μνήμη

Σε αυτό το κεφάλαιο, αρχίζουμε να εργαζόμαστε με τη Δευτερεύουσα μνήμη (ή με αρχεία). Η δευτερεύουσα μνήμη δεν διαγράφεται όταν η τροφοδοσία ρεύματος σταματήσει. Επίσης, στην περίπτωση μιας μονάδας flash USB, τα δεδομένα που γράφουμε με τα προγράμματά μας μπορούν να αφαιρεθούν από το σύστημά μας και να μεταφερθούν σε άλλο σύστημα.

Θα επικεντρωθούμε κυρίως στην ανάγνωση και τη σύνταξη αρχείων κειμένου όπως αυτά που δημιουργούμε σε ένα πρόγραμμα επεξεργασίας κειμένου. Αργότερα θα δούμε πώς δουλεύουμε με αρχεία βάσης δεδομένων που είναι δυαδικά αρχεία, ειδικά σχεδιασμένα για ανάγνωση και εγγραφή μέσω λογισμικού βάσης δεδομένων.

Άνοιγμα αρχείων

Όταν θέλουμε να διαβάσουμε ή να γράψουμε ένα αρχείο (ας πούμε στον σκληρό δίσκο), πρέπει πρώτα να ανοίξουμε (open) το αρχείο. Το άνοιγμα του αρχείου επικοινωνεί με το λειτουργικό σας σύστημα, το οποίο γνωρίζει πού αποθηκεύονται τα δεδομένα του κάθε αρχείου. Όταν ανοίγετε ένα αρχείο, ζητάτε από το λειτουργικό σύστημα να βρει το αρχείο βάση ονόματος και να βεβαιωθεί ότι το αρχείο υπάρχει. Σε αυτό το παράδειγμα, ανοίγουμε το αρχείο mbox.txt, το οποίο θα πρέπει να είναι αποθηκευμένο στον ίδιο φάκελο, στον οποίο βρίσκεστε όταν ξεκινάτε την Python. Μπορείτε να κάνετε λήψη αυτού του αρχείου από το www.gr.py4e.com/code3/mbox.txt

>>> fhand = open('mbox.txt')
>>> print(fhand)
<_io.TextIOWrapper name='mbox.txt' mode='r' encoding='cp1252'>

Εάν το open ολοκληρωθεί με επιτυχία, το λειτουργικό σύστημα μας επιστρέφει έναν περιγραφέα αρχείου (file handle). Ο περιγραφέας αρχείου δεν είναι τα πραγματικά δεδομένα που περιέχονται στο αρχείο, αλλά αντίθετα είναι μια “λαβή” που μπορούμε να χρησιμοποιήσουμε για να διαβάσουμε τα δεδομένα. Σας δίνεται ένας περιγραφέας εάν υπάρχει το ζητούμενο αρχείο και έχετε τα κατάλληλα δικαιώματα για να διαβάσετε το αρχείο.

Ένας Περιγραφέας Αρχείου (File Handle)
Ένας Περιγραφέας Αρχείου (File Handle)

Εάν το αρχείο δεν υπάρχει, το open θα αποτύχει με ένα traceback και δεν θα δημιουργηθεί περιγραφέας για πρόσβαση στα περιεχόμενα του αρχείου:

>>> fhand = open('stuff.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'stuff.txt'

Αργότερα θα χρησιμοποιήσουμε τα try και except για να αντιμετωπίσουμε, πιο χαριτωμένα, την κατάσταση όπου επιχειρούμε να ανοίξουμε ένα αρχείο που δεν υπάρχει.

Αρχεία κειμένου και γραμμές

Ένα αρχείο κειμένου μπορούν να θεωρηθεί ως μια ακολουθία γραμμών, όπως μια συμβολοσειρά Python μπορεί να θεωρηθεί ως μια ακολουθία χαρακτήρων. Για παράδειγμα, αυτό είναι ένα δείγμα αρχείου κειμένου που καταγράφει τη δραστηριότητα αλληλογραφίας από διάφορα άτομα σε μια ομάδα ανάπτυξης έργου ανοιχτού κώδικα:

From [email protected] Sat Jan  5 09:14:16 2008
Return-Path: <[email protected]>
Date: Sat, 5 Jan 2008 09:12:18 -0500
To: [email protected]
From: [email protected]
Subject: [sakai] svn commit: r39772 - content/branches/
Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772
...

Ολόκληρο το αρχείο αλληλεπιδράσεων αλληλογραφίας είναι διαθέσιμο στο

www.gr.py4e.com/code3/mbox.txt

και μια μικρότερη έκδοση του αρχείου είναι διαθέσιμη στο

www.gr.py4e.com/code3/mbox-short.txt

Αυτά τα αρχεία είναι σε τυπική μορφή αρχείου που περιέχει πολλά μηνύματα αλληλογραφίας. Οι γραμμές που ξεκινούν με “From (Από)” διαχωρίζουν τα μηνύματα και οι γραμμές που ξεκινούν με “From:” αποτελούν μέρος των μηνυμάτων. Για περισσότερες πληροφορίες σχετικά με τη μορφή mbox, ανατρέξτε στο https://en.wikipedia.org/wiki/Mbox.

Για να χωρίσετε το αρχείο σε γραμμές, υπάρχει ένας ειδικός χαρακτήρας που αντιπροσωπεύει το “τέλος της γραμμής” που ονομάζεται χαρακτήρας newline.

Στην Python, σε σταθερές συμβολοσειρών, αντιπροσωπεύουμε τον χαρακτήρα newline ως ανάστροφη κάθετο-n (). Παρόλο που αυτό μοιάζει με δύο χαρακτήρες, είναι στην πραγματικότητα ένας μόνο χαρακτήρας. Όταν εξετάζουμε τη μεταβλητή πληκτρολογώντας “stuff” στον διερμηνέα, μας δείχνει το \n στη συμβολοσειρά, αλλά όταν χρησιμοποιούμε την print για να εμφανίσουμε τη συμβολοσειρά, βλέπουμε τη συμβολοσειρά σπασμένη σε δύο γραμμές από τον χαρακτήρα νέας γραμμής.

>>> stuff = 'Hello\nWorld!'
>>> stuff
'Hello\nWorld!'
>>> print(stuff)
Hello
World!
>>> stuff = 'X\nY'
>>> print(stuff)
X
Y
>>> len(stuff)
3

Μπορείτε επίσης να δείτε ότι το μήκος της συμβολοσειράς X\nY είναι τρεις χαρακτήρες, επειδή ο χαρακτήρας νέας γραμμής είναι ένας χαρακτήρας.

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

Ο χαρακτήρας νέας γραμμής, λοιπόν, διαχωρίζει τους χαρακτήρες του αρχείου σε γραμμές.

Ανάγνωση αρχείων

Ενώ ο περιγραφέας αρχείου δεν περιέχει τα δεδομένα για το αρχείο, είναι πολύ εύκολο να τον χρησιμοποιήσετε και να δημιουργήσετε έναν βρόχο for για να διαβάσετε και να μετρήσετε κάθε μία από τις γραμμές σε ένα αρχείο:

fhand = open('mbox-short.txt')
πλήθος = 0
for γραμμή in fhand:
    πλήθος = πλήθος + 1
print('Πλήθος γραμμών:', πλήθος)

# Code: http://www.py4e.com/code3/open.py

Μπορούμε να χρησιμοποιήσουμε τον περιγραφέα αρχείου ως μια ακολουθία στον βρόχο for. Ο παραπάνω βρόχος for, απλώς μετράει τον αριθμό των γραμμών στο αρχείο και τις εκτυπώνει. Η κατά προσέγγιση μετάφραση του βρόχου for στα αγγλικά είναι, "για κάθε γραμμή στο αρχείο που αντιπροσωπεύεται από τον περιγραφέα του αρχείου, προσθέστε ένα στη μεταβλητή count.

Ο λόγος που η συνάρτηση open δεν διαβάζει ολόκληρο το αρχείο είναι ότι το αρχείο μπορεί να είναι αρκετά μεγάλο, με πολλά gigabyte δεδομένων. Η δήλωση open χρειάζεται τον ίδιο χρόνο ανεξάρτητα από το μέγεθος του αρχείου. Ο βρόχος for είναι αυτός που προκαλεί, στην πραγματικότητα, την ανάγνωση των δεδομένων από το αρχείο.

Όταν διαβάζεται το αρχείο χρησιμοποιώντας έναν βρόχο for, με αυτόν τον τρόπο, η Python φροντίζει να διασπάσει τα δεδομένα του αρχείου σε ξεχωριστές γραμμές χρησιμοποιώντας τον χαρακτήρα νέας γραμμής. Η Python διαβάζει κάθε γραμμή μέσω του χαρακτήρα νέας γραμμής και περιλαμβάνει το χαρακτήρα νέας γραμμής, ως τον τελευταίο χαρακτήρα, στη μεταβλητή γραμμή σε κάθε επανάληψη του βρόχου for.

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

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

>>> fhand = open('mbox-short.txt')
>>> inp = fhand.read()
>>> print(len(inp))
94626
>>> print(inp[:20])
From stephen.marquar

Σε αυτό το παράδειγμα, ολόκληρο το περιεχόμενο (και οι 94.626 χαρακτήρες) του αρχείου mbox-short.txt διαβάζονται απευθείας στη μεταβλητή inp. Χρησιμοποιούμε διαμέριση συμβολοσειράς για να εκτυπώσουμε τους πρώτους 20 χαρακτήρες των δεδομένων συμβολοσειράς που είναι αποθηκευμένα στο inp.

Όταν το αρχείο διαβάζεται με αυτόν τον τρόπο, όλοι οι χαρακτήρες, συμπεριλαμβανομένων όλων των γραμμών και χαρακτήρων νέας γραμμής, είναι μια μεγάλη συμβολοσειρά στη μεταβλητή inp. Είναι καλή ιδέα να αποθηκεύεται η έξοδος του read ως μεταβλητή, επειδή κάθε κλήση της read εξαντλεί τους πόρους:

>>> fhand = open('mbox-short.txt')
>>> print(len(fhand.read()))
94626
>>> print(len(fhand.read()))
0

Θυμηθείτε ότι αυτή η μορφή της συνάρτησης open θα πρέπει να χρησιμοποιείται μόνο εάν τα δεδομένα του αρχείου χωράνε άνετα στην κύρια μνήμη του υπολογιστή σας. Εάν το αρχείο είναι πολύ μεγάλο για να χωρέσει στην κύρια μνήμη, θα πρέπει να γράψετε το πρόγραμμά σας έτσι ώστε να διαβάσει το αρχείο σε κομμάτια, χρησιμοποιώντας έναν βρόχο for ή while.

Φιλτράρισμα αρχείου

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

Για παράδειγμα, αν θέλαμε να διαβάσουμε ένα αρχείο και να εκτυπώσουμε μόνο τις γραμμές που αρχίζουν με το πρόθεμα “From:”, θα μπορούσαμε να χρησιμοποιήσουμε τη μέθοδο συμβολοσειράς startswith για να επιλέξουμε μόνο τις γραμμές με το επιθυμητό πρόθεμα:

fhand = open('mbox-short.txt')
for γραμμή in fhand:
    if γραμμή.startswith('From:'):
        print(γραμμή)

# Code: http://www.py4e.com/code3/search1.py

Όταν εκτελείται αυτό το πρόγραμμα, έχουμε την ακόλουθη έξοδο:

From: [email protected]

From: [email protected]

From: [email protected]

From: [email protected]
...

Η έξοδος φαίνεται υπέροχη αφού οι μόνες γραμμές που βλέπουμε είναι αυτές που ξεκινούν με “From:”, αλλά γιατί βλέπουμε τις επιπλέον κενές γραμμές; Αυτό οφείλεται στον αόρατο χαρακτήρα newline. Κάθε μία από τις γραμμές τελειώνει με έναν χαρακτήρα νέας γραμμής, επομένως η δήλωση print εκτυπώνει τη συμβολοσειρά της μεταβλητή γραμμή, που όμως περιλαμβάνει έναν χαρακτήρα νέας γραμμής και στη συνέχεια η print προσθέτει άλλη μία νέα γραμμή, με αποτέλεσμα το εφέ διπλού διαστήματος που βλέπουμε.

Θα μπορούσαμε να χρησιμοποιήσουμε την διαμέριση γραμμής, για να εκτυπώσουμε τους χαρακτήρες εκτός από τον τελευταίο, αλλά μια απλούστερη προσέγγιση είναι να χρησιμοποιήσουμε τη μέθοδο rstrip που αφαιρεί τους λευκούς χαρακτήρες από τη δεξιά πλευρά μιας συμβολοσειράς ως εξής:

fhand = open('mbox-short.txt')
for γραμμή in fhand:
    γραμμή = γραμμή.rstrip()
    if γραμμή.startswith('From:'):
        print(γραμμή)

# Code: http://www.py4e.com/code3/search2.py

Όταν εκτελείται αυτό το πρόγραμμα, έχουμε την ακόλουθη έξοδο:

From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
...

Καθώς τα προγράμματα επεξεργασίας αρχείων σας γίνονται πιο περίπλοκα, μπορεί να θελήσετε να δομήσετε τους βρόχους αναζήτησης χρησιμοποιώντας το continue. Η βασική ιδέα του βρόχου αναζήτησης είναι ότι ψάχνετε για “ενδιαφέρουσες” γραμμές και ουσιαστικά παρακάμπτετε τις “αδιάφορες” γραμμές. Και μετά, όταν βρίσκουμε μια ενδιαφέρουσα γραμμή, κάνουμε κάτι με αυτή τη γραμμή.

Μπορούμε να δομήσουμε τον βρόχο για να ακολουθήσουμε το μοτίβο της παράκαμψης αδιάφορων γραμμών ως εξής:

fhand = open('mbox-short.txt')
for γραμμή in fhand:
    γραμμή = γραμμή.rstrip()
    # Παράκαμψη `αδιάφορων` γραμμών
    if not γραμμή.startswith('From:'):
        continue
    # Επεξεργασία 'ενδιαφερόντων' γραμμών
    print(γραμμή)

# Code: http://www.py4e.com/code3/search3.py

Η έξοδος του προγράμματος είναι η ίδια. Στα αγγλικά, οι αδιάφορες γραμμές είναι εκείνες που δεν ξεκινούν με “From:”, τις οποίες παραλείπουμε χρησιμοποιώντας το continue. Για τις “ενδιαφέρουσες” γραμμές (δηλαδή αυτές που ξεκινούν με “From:”) εκτελούμε την επεξεργασία σε αυτές τις γραμμές.

Μπορούμε να χρησιμοποιήσουμε τη μέθοδο συμβολοσειράς find για να προσομοιώσουμε την αναζήτηση των προγραμμάτων επεξεργασίας κειμένου, που βρίσκει γραμμές στις οποίες περιλαμβάνεται, οπουδήποτε, η συμβολοσειρά αναζήτησης. Μιας και το find αναζητά την εμφάνιση μιας συμβολοσειράς μέσα σε μια άλλη συμβολοσειρά και είτε επιστρέφει τη θέση της συμβολοσειράς είτε -1, εάν η συμβολοσειρά δεν βρέθηκε, μπορούμε να γράψουμε τον ακόλουθο βρόχο για να εμφανίσουμε τις γραμμές που περιέχουν τη συμβολοσειρά “@uct.ac .za” (δηλαδή, προέρχονται από το Πανεπιστήμιο του Κέιπ Τάουν στη Νότια Αφρική):

fhand = open('mbox-short.txt')
for γραμμή in fhand:
    γραμμή = γραμμή.rstrip()
    if γραμμή.find('@uct.ac.za') == -1: continue
    print(γραμμή)

# Code: http://www.py4e.com/code3/search4.py

Ο οποίος παράγει την ακόλουθη έξοδο:

From [email protected] Sat Jan  5 09:14:16 2008
X-Authentication-Warning: set sender to [email protected] using -f
From: [email protected]
Author: [email protected]
From [email protected] Fri Jan  4 07:02:32 2008
X-Authentication-Warning: set sender to [email protected] using -f
From: [email protected]
Author: [email protected]
...

Εδώ χρησιμοποιούμε επίσης τη σύντομη μορφή της εντολής if, όπου βάζουμε το continue στην ίδια γραμμή με το if. Αυτή η σύντομη μορφή του if λειτουργεί το ίδιο όπως αν το continue να ήταν στην επόμενη γραμμή και με εσοχή.

Επιτρέποντας στον χρήστη να επιλέξει το όνομα του αρχείου

Πραγματικά, δεν θέλουμε να πρέπει να επεξεργαζόμαστε τον κώδικά μας σε Python κάθε φορά που θέλουμε να επεξεργαστούμε ένα διαφορετικό αρχείο. Θα ήταν πιο βολικό να ζητάμε από τον χρήστη να εισάγει τη συμβολοσειρά ονόματος αρχείου, κάθε φορά που εκτελείται το πρόγραμμα, ώστε να μπορεί να χρησιμοποιήσει το πρόγραμμά μας σε διαφορετικά αρχεία, χωρίς να αλλάξει τον κώδικα της Python.

Αυτό είναι πολύ απλό να υλοποιηθεί, διαβάζοντας το όνομα του αρχείου από τον χρήστη, χρησιμοποιώντας την input ως εξής:

fname = input('Εισαγάγετε το όνομα του αρχείου: ')
fhand = open(fname)
πλήθος = 0
for γραμμή in fhand:
    if γραμμή.startswith('Subject:'):
        πλήθος = πλήθος + 1
print('Υπάρχουν ', πλήθος, ' γραμμές θέματος στο ', fname)

# Code: http://www.py4e.com/code3/search6.py

Διαβάζουμε το όνομα του αρχείου από τον χρήστη και το τοποθετούμε σε μια μεταβλητή με το όνομα fname και ανοίγουμε αυτό το αρχείο. Τώρα μπορούμε να εκτελέσουμε το πρόγραμμα, επανειλημμένα, για διαφορετικά αρχεία.

python search6.py
Εισαγάγετε το όνομα του αρχείου: mbox.txt
Υπάρχουν 1797 γραμμές θέματος στο mbox.txt

python search6.py
Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
Υπάρχουν 27 γραμμές θέματος στο mbox-short.txt

Πριν κρυφοκοιτάξετε την επόμενη ενότητα, ρίξτε μια ματιά στο παραπάνω πρόγραμμα και αναρωτηθείτε, “Τι θα μπορούσε να πάει στραβά εδώ;” ή “Τι μπορεί να κάνει ο φιλικός χρήστης μας, που θα έκανε το όμορφο μικρό μας πρόγραμμα να σταματήσει, άχαρα, την εκτέλεσή του με ένα traceback, κάνοντάς μας να φαινόμαστε όχι και τόσο καλοί στα μάτια των χρηστών μας;”

Χρήση try, except, και open

Σας είπα να μην κρυφοκοιτάξετε. Αυτή είναι η τελευταία σας ευκαιρία.

Τι γίνεται αν ο χρήστης μας πληκτρολογήσει κάτι που δεν είναι όνομα αρχείου;

python search6.py
Εισαγάγετε το όνομα του αρχείου: missing.txt
Traceback (most recent call last):
  File "search6.py", line 2, in <module>
    fhand = open(fname)
FileNotFoundError: [Errno 2] No such file or directory: 'missing.txt'

python search6.py
Εισαγάγετε το όνομα του αρχείου: na na boo boo
Traceback (most recent call last):
  File "search6.py", line 2, in <module>
    fhand = open(fname)
FileNotFoundError: [Errno 2] No such file or directory: 'na na boo boo'

Μην γελάτε. Οι χρήστες θα κάνουν τελικά ό,τι είναι δυνατό για να κολλήσουν τα προγράμματά σας, είτε κατά λάθος είτε με κακόβουλη πρόθεση. Στην πραγματικότητα, ένα σημαντικό μέρος οποιασδήποτε ομάδας ανάπτυξης λογισμικού είναι ένα άτομο ή μια ομάδα ατόμων, που ονομάζεται Quality Assurance (Διασφάλιση Ποιότητας) (ή QA για συντομία), των οποίων η ίδια δουλειά είναι να κάνουν τα πιο τρελά πράγματα σε μια προσπάθεια να “σπάσουν” το λογισμικό που ο προγραμματιστής δημιούργησε.

Η ομάδα QA είναι υπεύθυνη για την εύρεση των ελαττωμάτων στα προγράμματα προτού παραδώσουμε το πρόγραμμα στους τελικούς χρήστες, που μπορεί να αγοράσουν το λογισμικό ή να πληρώσουν το μισθό μας για τη σύνταξη του λογισμικού. Έτσι, η ομάδα QA είναι ο καλύτερος φίλος του προγραμματιστή.

Τώρα λοιπόν που βλέπουμε το ψεγάδι στο πρόγραμμα, μπορούμε να το διορθώσουμε, κομψά, χρησιμοποιώντας τη δομή try/except. Πρέπει να αντιληφθούμε ότι η κλήση της open ενδέχεται να αποτύχει και να προσθέσουμε κατάλληλο κώδικα ανάκτησης όταν η open αποτύχει ως εξής:

fname = input('Εισαγάγετε το όνομα του αρχείου: ')
try:
    fhand = open(fname)
except:
    print('Δεν είναι δυνατό το άνοιγμα του αρχείου:', fname)
    exit()
πλήθος = 0
for γραμμή in fhand:
    if γραμμή.startswith('Subject:'):
        πλήθος = πλήθος + 1
print('Υπάρχουν ', πλήθος, ' γραμμές θέματος στο ', fname)

# Code: http://www.py4e.com/code3/search7.py

Η συνάρτηση exit τερματίζει το πρόγραμμα. Είναι μια συνάρτηση που καλούμε και δεν επιστρέφει ποτέ. Τώρα, όταν ο χρήστης μας (ή η ομάδα QA) πληκτρολογεί ανόητα ή λάθος ονόματα αρχείων, τα “πιάνουμε” και τα ξεπερνάμε με χάρη:

python search7.py
Εισαγάγετε το όνομα του αρχείου: mbox.txt
Υπάρχουν 1797 γραμμές θέματος στο mbox.txt

python search7.py
Εισαγάγετε το όνομα του αρχείου: na na boo boo
Δεν είναι δυνατό το άνοιγμα του αρχείου: na na boo boo

Η προστασία της κλήσης open είναι ένα καλό παράδειγμα της σωστής χρήσης των try και except σε ένα πρόγραμμα Python. Χρησιμοποιούμε τον όρο “Pythonic” όταν κάνουμε κάτι με τον “τρόπο της Python”. Θα μπορούσαμε να πούμε ότι το παραπάνω παράδειγμα είναι ο Pythonic τρόπος για να ανοίξετε ένα αρχείο.

Μόλις γίνετε πιο ειδικοί στην Python, μπορείτε να συμμετάσχετε σε μονομαχίες με άλλους προγραμματιστές Python για να αποφασίσετε ποια από τις δύο ισοδύναμες λύσεις σε ένα πρόβλημα είναι “πιο Python”. Ο στόχος να είσαι «πιο Pythonic» αντικατοπτρίζει την ιδέα ότι ο προγραμματισμός είναι εν μέρει μηχανική και εν μέρει τέχνη. Δεν μας ενδιαφέρει πάντα να κάνουμε κάτι που λειτουργεί, θέλουμε επίσης η λύση μας να είναι κομψή και να εκτιμάται ως κομψή από τους ομοτίμους μας.

Γραφή σε αρχεία

Για να γράψετε ένα αρχείο, πρέπει να το ανοίξετε με τη λειτουργία “w” ως δεύτερη παράμετρο:

>>> fout = open('output.txt', 'w')
>>> print(fout)
<_io.TextIOWrapper name='output.txt' mode='w' encoding='cp1252'>

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

Η μέθοδος write του αντικειμένου χειρισμού αρχείου τοποθετεί δεδομένα στο αρχείο, επιστρέφοντας τον αριθμό των χαρακτήρων που γράφτηκαν. Η προεπιλεγμένη λειτουργία εγγραφής είναι κείμενο για εγγραφή (και ανάγνωση) συμβολοσειρών.

>>> γραμμή1 = "Αυτό το κλαδί, είναι\n"
>>> fout.write(γραμμή1)
21

Και πάλι, το αντικείμενο αρχείου παρακολουθεί τη θέση του, οπότε αν καλέσετε ξανά την write, προσθέτει τα νέα δεδομένα στο τέλος.

Πρέπει να φροντίσουμε να διαχειριζόμαστε τα άκρα των γραμμών, καθώς γράφουμε στο αρχείο, εισάγοντας ρητά τον χαρακτήρα νέας γραμμής όταν θέλουμε να τερματίσουμε μια γραμμή. Η εντολή print προσθέτει αυτόματα μια νέα γραμμή, αλλά η μέθοδος write δεν προσθέτει τη νέα γραμμή αυτόματα.

>>> γραμμή2 = 'το έμβλημα του τόπου μας.\n'
>>> fout.write(γραμμή2)
26

Όταν ολοκληρώσετε τη σύνταξη, πρέπει να κλείσετε το αρχείο για να βεβαιωθείτε ότι και το τελευταίο bit δεδομένων είναι φυσικά γραμμένο στο δίσκο, ώστε να μην χαθεί εάν διακοπεί η τροφοδοσία ρεύμετος.

>>> fout.close()

Θα μπορούσαμε να κλείσουμε και τα αρχεία που ανοίγουμε για ανάγνωση, αλλά μπορεί να είμαστε και λίγο απρόσεκτοι αν μόνο ανοίγουμε κάποια αρχεία, καθώς η Python φροντίζει ώστε όλα τα ανοιχτά αρχεία να κλείνουν όταν τελειώνει το πρόγραμμα. Όταν γράφουμε όμως σε αρχεία, θέλουμε να τα κλείνουμε ξεκάθαρα, για να μην αφήνουμε τίποτα στην τύχη.

Εκσφαλμάτωση

Όταν διαβάζετε και γράφετε αρχεία, ενδέχεται να αντιμετωπίσετε προβλήματα με τους λευκούς χαρακτήρες. Αυτά τα σφάλματα μπορεί να είναι δύσκολο να εντοπιστούν, επειδή τα κενά, τα tab και οι νέες γραμμές είναι συνήθως αόρατα:

>>> s = '1 2\t 3\n 4'
>>> print(s)
1 2  3
 4

Η ενσωματωμένη συνάρτηση repr μπορεί να βοηθήσει. Λαμβάνει οποιοδήποτε αντικείμενο ως όρισμα και επιστρέφει μια παράσταση συμβολοσειράς του αντικειμένου. Για συμβολοσειρές, αναπαρηστά τους χαρακτήρες κενού διαστήματος με ακολουθίες ανάστροφης κάθετης:

>>> print(repr(s))
'1 2\t 3\n 4'

Αυτό μπορεί να είναι χρήσιμο για τον εντοπισμό σφαλμάτων.

Ένα άλλο πρόβλημα που μπορεί να αντιμετωπίσετε είναι ότι διαφορετικά συστήματα χρησιμοποιούν διαφορετικούς χαρακτήρες για να υποδείξουν το τέλος μιας γραμμής. Ορισμένα συστήματα χρησιμοποιούν μια νέα γραμμή, που αναπαρίσταται με “”. Άλλα χρησιμοποιούν έναν χαρακτήρα επιστροφής, που αναπαρίσταται με «. Κάποια χρησιμοποιούν και τα δύο. Εάν μετακινείτε αρχεία μεταξύ διαφορετικών συστημάτων, αυτές οι ασυνέπειες ενδέχεται να προκαλέσουν προβλήματα.

Για τα περισσότερα συστήματα, υπάρχουν εφαρμογές για μετατροπή από τη μια μορφή στην άλλη. Μπορείτε να τα βρείτε (και να διαβάσετε περισσότερα για αυτό το ζήτημα) στη διεύθυνση https://www.wikipedia.org/wiki/Newline. Ή, φυσικά, θα μπορούσατε να γράψετε μία μόνοι σας.

Γλωσσάριο

catch
Για να αποτρέψετε μια εξαίρεση από τον τερματισμό ενός προγράμματος χρησιμοποιώντας την εντολή try και except.
Pythonic
Μια τεχνική που λειτουργεί κομψά στην Python. “Η χρήση του try και except είναι ο Pythonic τρόπος ανάκτησης από αρχεία που λείπουν”.
Quality Assurance - Διασφάλιση Ποιότητας
Ένα άτομο ή μια ομάδα που επικεντρώνεται στη διασφάλιση της συνολικής ποιότητας ενός προϊόντος λογισμικού. Το QA συχνά εμπλέκεται στη δοκιμή ενός προϊόντος και στον εντοπισμό προβλημάτων πριν από την κυκλοφορία του προϊόντος.
αρχείο κειμένου
Μια ακολουθία χαρακτήρων που είναι αποθηκευμένοι σε μονάδα μόνιμη αποθήκευση, όπως ένας σκληρός δίσκος.
νέα γραμμή
Ένας ειδικός χαρακτήρας που χρησιμοποιείται σε αρχεία και συμβολοσειρές για να υποδείξει το τέλος μιας γραμμής.

Ασκήσεις

Άσκηση 1: Γράψτε ένα πρόγραμμα για να διαβάσετε ένα αρχείο και να εκτυπώσετε τα περιεχόμενα του αρχείου (γραμμή προς γραμμή) όλα με κεφαλαία. Η εκτέλεση του προγράμματος θα έχει ως εξής:

python shout.py
Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
FROM [email protected] SAT JAN  5 09:14:16 2008
RETURN-PATH: <[email protected]>
RECEIVED: FROM MURDER (MAIL.UMICH.EDU [141.211.14.90])
     BY FRANKENSTEIN.MAIL.UMICH.EDU (CYRUS V2.3.8) WITH LMTPA;
     SAT, 05 JAN 2008 09:14:16 -0500

Μπορείτε να κατεβάσετε το αρχείο από www.gr.py4e.com/code3/mbox-short.txt

Άσκηση 2: Γράψτε ένα πρόγραμμα που να δέχεται ένα όνομα αρχείου και, στη συνέχεια, να διαβάζει το αρχείο αυτό και να αναζητά γραμμές της μορφής:

X-DSPAM-Confidence: 0.8475

Όταν συναντήσετε μια γραμμή που ξεκινά με “X-DSPAM-Confidence:” διασπάστε τη γραμμή για να εξαγάγετε τον αριθμό κινητής υποδιαστολής στη γραμμή. Μετρήστε αυτές τις γραμμές και στη συνέχεια υπολογίστε το σύνολο των τιμών “spam confidence” από αυτές τις γραμμές. Όταν φτάσετε στο τέλος του αρχείου, εκτυπώστε τη μέση τιμή των τιμών “spam confidence”.

Εισαγάγετε το όνομα του αρχείου: mbox.txt
Μέσο spam confidence: 0.894128046745

Εισαγάγετε το όνομα του αρχείου: mbox-short.txt
Μέσο spam confidence: 0.750718518519

Δοκιμάστε το αρχείο σας στα αρχεία mbox.txt και mbox-short.txt.

Άσκηση 3: Μερικές φορές, όταν οι προγραμματιστές βαριούνται ή θέλουν να διασκεδάσουν λίγο, προσθέτουν ένα αβλαβές Πασχαλινό Αυγό (Easter Egg) στο πρόγραμμά τους. Τροποποιήστε το πρόγραμμα που ζητά από τον χρήστη το όνομα του αρχείου, έτσι ώστε να εκτυπώνει ένα αστείο μήνυμα όταν ο χρήστης πληκτρολογεί το ακριβές όνομα αρχείου “na na boo boo”. Το πρόγραμμα θα πρέπει να συμπεριφέρεται κανονικά για όλα τα άλλα αρχεία που υπάρχουν και δεν υπάρχουν. Ακολουθεί ένα δείγμα εκτέλεσης του προγράμματος:

python egg.py
Enter the file name: mbox.txt
There were 1797 subject lines in mbox.txt

python egg.py
Εισαγάγετε το όνομα του αρχείου: missing.tyxt
Δεν είναι δυνατό το άνοιγμα του αρχείου: missing.tyxt

python egg.py
Εισαγάγετε το όνομα του αρχείου: na na boo boo
NA NA BOO BOO TO YOU - You have been punk'd!

Δεν σας ενθαρρύνουμε να βάλετε τα “Easter Eggs” στα προγράμματά σας. Aυτό είναι απλώς μια άσκηση.


Αν εντοπίσετε κάποιο λάθος σε αυτό το βιβλίο μην διστάσετε να μου στείλετε τη διόρθωση στο Github.