7. Είσοδος και Έξοδος
*********************

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


7.1. Ομορφότερη Μορφοποίηση Εξόδου
==================================

Μέχρι στιγμής έχουμε συναντήσει δύο τρόπους γραφής τιμών: *expression
statements* και τη συνάρτηση "print()".  (Ένας τρίτος τρόπος είναι η
χρήση της μεθόδου "write()" των αντικειμένων αρχείων· το standard
αρχείο εξόδου μπορεί να αναφέρεται ως "sys.stdout". Δείτε την Αναφορά
Βιβλιοθήκης για περισσότερες πληροφορίες σχετικά με αυτό.)

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

* Για να χρησιμοποιήσετε formatted string literals, ξεκινήστε μια
  συμβολοσειρά με "f" or "F" πριν από το αρχικό εισαγωγικό ή το τριπλό
  εισαγωγικό. Μέσα σε αυτήν την συμβολοσειρά, μπορείτε να γράψετε μια
  έκφραση Python μεταξύ χαρακτήρων "{" και "}" που μπορεί να
  αναφέρεται σε μεταβλητές ή κυριολεκτικές τιμές.

     >>> year = 2016
     >>> event = 'Referendum'
     >>> f'Results of the {year} {event}'
     'Results of the 2016 Referendum'

* Η μέθοδος "str.format()" των συμβολοσειρών απαιτεί περισσότερη μη
  χειροκίνητη προσπάθεια.  Θα εξακολουθείτε να χρησιμοποιείτε τα "{"
  και "}" για να επισημάνετε που θα αντικατασταθεί μια μεταβλητή και
  μπορεί να παρέχει λεπτομερείς οδηγίες μορφοποίησης, αλλά θα
  χρειαστεί επίσης να παρέχετε τις πληροφορίες που θα μορφοποιήσετε.
  Στο παρακάτω μπλοκ κώδικα υπάρχουν δύο παραδείγματα για τον τρόπο
  μορφοποίησης μεταβλητών:

     >>> yes_votes = 42_572_654
     >>> total_votes = 85_705_149
     >>> percentage = yes_votes / total_votes
     >>> '{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)
     ' 42572654 YES votes  49.67%'

  Παρατηρήστε πως τα "yes_votes" συμπληρώνονται με κενά και αρνητικό
  πρόσημο μόνο για αρνητικούς αριθμούς. Το παράδειγμα εκτυπώνει επίσης
  το "percentage" πολλαπλασιασμένο επί 100, με 2 δεκαδικά ψηφία και
  ακολουθούμενο από ένα σύμβολο ποσοστού (δείτε το Format
  Specification Mini-Language για λεπτομέρειες).

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

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

Η συνάρτηση "str()" προορίζεται να επιστρέφει αναπαραστάσεις τιμών που
είναι αρκετά αναγνώσιμες από τον άνθρωπο, ενώ το "repr()" προορίζεται
για τη δημιουργία αναπαραστάσεων που μπορούν να διαβαστούν από τον
διερμηνέα (ή θα επιβάλουν ένα "SyntaxError" αν δεν υπάρχει ισοδύναμη
σύνταξη).  Για αντικείμενα που δεν έχουν συγκεκριμένη αναπαράσταση για
ανθρώπινη κατανάλωση, η "str()" θα επιστρέψει την ίδια τιμή με το
"repr()".  Πολλές τιμές, όπως αριθμοί ή δομές όπως λίστες και λεξικά,
έχουν την ίδια αναπαράσταση χρησιμοποιώντας οποιαδήποτε συνάρτηση. Τα
strings, συγκεκριμένα, έχουν δύο διακριτές παραστάσεις.

Μερικά παραδείγματα:

   >>> s = 'Hello, world.'
   >>> str(s)
   'Hello, world.'
   >>> repr(s)
   "'Hello, world.'"
   >>> str(1/7)
   '0.14285714285714285'
   >>> x = 10 * 3.25
   >>> y = 200 * 200
   >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
   >>> print(s)
   The value of x is 32.5, and y is 40000...
   >>> # The repr() of a string adds string quotes and backslashes:
   >>> hello = 'hello, world\n'
   >>> hellos = repr(hello)
   >>> print(hellos)
   'hello, world\n'
   >>> # The argument to repr() may be any Python object:
   >>> repr((x, y, ('spam', 'eggs')))
   "(32.5, 40000, ('spam', 'eggs'))"

The "string" module contains support for a simple templating approach
based upon regular expressions, via "string.Template". This offers yet
another way to substitute values into strings, using placeholders like
"$x" and replacing them with values from a dictionary. This syntax is
easy to use, although it offers much less control for formatting.


7.1.1. Μορφοποιημένα String Literals
------------------------------------

Τα Formatted string literals (ονομάζονται επίσης f-strings για
συντομία) σας επιτρέπουν να συμπεριλάβετε την τιμή των εκφράσεων
Python μέσα σε μια συμβολοσειρά, θέτοντας πρόθεμα στη συμβολοσειρά με
"f" ή "F" και γράφοντας εκφράσεις ως "{expression}".

Ένας προαιρετικός αναθέτης (specifier) μορφής μπορεί να ακολουθεί την
έκφραση. Αυτό επιτρέπει μεγαλύτερο έλεγχο στον τρόπο μορφοποίησης της
τιμής. Το παρακάτω παράδειγμα στρογγυλοποιεί το pi σε τρία ψηφία μετά
το δεκαδικό:

   >>> import math
   >>> print(f'The value of pi is approximately {math.pi:.3f}.')
   The value of pi is approximately 3.142.

Η μετάδοση ενός ακέραιου αριθμού μετά το "':'" θα έχει ως αποτέλεσμα
αυτό το πεδίο να έχει πλάτος ελάχιστου αριθμού χαρακτήρων.  Αυτό είναι
χρήσιμο για την ευθυγράμμιση στηλών.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for name, phone in table.items():
   ...     print(f'{name:10} ==> {phone:10d}')
   ...
   Sjoerd     ==>       4127
   Jack       ==>       4098
   Dcab       ==>       7678

Μπορούν να χρησιμοποιηθούν άλλοι τροποποιητές για την μετατροπή της
τιμής πριν τη μορφοποίηση της. Το "'!a'" ισχύει για "ascii()", το
"'!s'" ισχύει για "str()", και το "'!r'" ισχύει για "repr()":

   >>> animals = 'eels'
   >>> print(f'My hovercraft is full of {animals}.')
   My hovercraft is full of eels.
   >>> print(f'My hovercraft is full of {animals!r}.')
   My hovercraft is full of 'eels'.

Ο αναθέτης (specifier) "=" μπορεί να χρησιμοποιηθεί για να επεκτείνει
μια έκφραση στο κείμενο της έκφρασης, ένα σύμβολο ίσο, και μετά την
αναπαράσταση της αξιολογούμενης έκφρασης:

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

Δείτε το self-documenting expressions για περισσότερες πληροφορίες
σχετικά με τον αναθέτη (specifier) "=". Για αναφορά σε αυτές τις
προδιαγραφές μορφής, ανατρέξτε στον οδηγό αναφοράς για το Format
Specification Mini-Language.


7.1.2. Η μέθοδος String format()
--------------------------------

Η βασική χρήση της μεθόδου "str.format()" μοιάζει με αυτό:

   >>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
   We are the knights who say "Ni!"

Οι αγκύλες και οι χαρακτήρες μέσα σε αυτές (που ονομάζονται πεδία
μορφής) αντικαθίστανται με τα αντικείμενα που μεταβιβάζονται στη
μέθοδο "str.format()".  Ένας αριθμός στις αγκύλες μπορεί να
χρησιμοποιηθεί για να αναφέρεται στη θέση του αντικειμένου που
μεταβιβάζεται στη μέθοδο "str.format()".

   >>> print('{0} and {1}'.format('spam', 'eggs'))
   spam and eggs
   >>> print('{1} and {0}'.format('spam', 'eggs'))
   eggs and spam

Εάν χρησιμοποιούνται keyword ορίσματα στη μέθοδο "str.format()", οι
τιμές τους αναφέρονται χρησιμοποιώντας το όνομα του ορίσματος.

   >>> print('This {food} is {adjective}.'.format(
   ...       food='spam', adjective='absolutely horrible'))
   This spam is absolutely horrible.

Τα ορίσματα θέσης και λέξης-κλειδιού μπορούν να συνδυαστούν αυθαίρετα:

   >>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
   ...                                                    other='Georg'))
   The story of Bill, Manfred, and Georg.

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

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...       'Dcab: {0[Dcab]:d}'.format(table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Αυτό θα μπορούσε επίσης να γίνει περνώντας το λεξικό "table" ως
ορίσματα λέξεων-κλειδιών με την σημείωση "**".

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Αυτό είναι ιδιαίτερα χρήσιμο σε συνδυασμό με την ενσωματωμένη (built-
in) συνάρτηση "vars()", η οποία επιστρέφει ένα λεξικό που περιέχει
όλες τις τοπικές μεταβλητές:

   >>> table = {k: str(v) for k, v in vars().items()}
   >>> message = " ".join([f'{k}: ' + '{' + k +'};' for k in table.keys()])
   >>> print(message.format(**table))
   __name__: __main__; __doc__: None; __package__: None; __loader__: ...

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

   >>> for x in range(1, 11):
   ...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

Για μια πλήρη επισκόπηση της μορφοποίησης συμβολοσειρών με
"str.format()", δείτε Format String Syntax.


7.1.3. Χειροκίνητη Μορφοποίηση Συμβολοσειρών
--------------------------------------------

Ακολουθεί ο ίδιος πίνακας τετραγώνων και κύβων, μορφοποιημένος
χειροκίνητα:

   >>> for x in range(1, 11):
   ...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
   ...     # Note use of 'end' on previous line
   ...     print(repr(x*x*x).rjust(4))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Σημειώστε ότι το ένα κενό μεταξύ κάθε στήλης προστέθηκε με τον τρόπο
που λειτουργεί το "print()": προσθέτει πάντα κενά μεταξύ των ορισμάτων
του.)

Η μέθοδος "str.rjust()" των αντικειμένων συμβολοσειράς τοποθετεί δεξιά
μια συμβολοσειρά σε ένα πεδίο δεδομένου πλάτους συμπληρώνοντας την με
κενά στα αριστερά. Υπάρχουν παρόμοιες μέθοδοι "str.ljust()" και
"str.center()". Αυτές οι μέθοδοι δεν γράφουν τίποτα, απλώς επιστρέφουν
μια συμβολοσειρά. Εάν η συμβολοσειρά εισόδου είναι πολύ μεγάλη, δεν
την περικόπτουν, αλλά την επιστρέφουν αμετάβλητη· αυτό θα μπερδέψει τη
διάταξη της στήλης σας, αλλά αυτό είναι συνήθως καλύτερο από την
εναλλακτική, που θα ήταν ψέματα για μια τιμή. (Αν θέλετε πραγματικά
περικοπή, μπορείτε πάντα να προσθέσετε μια λειτουργία slice, όπως στο
"x.ljust(n)[:n]".)

Υπάρχει μια άλλη μέθοδος, η "str.zfill()", η οποία συμπληρώνει μια
αριθμητική συμβολοσειρά στα αριστερά με μηδενικά.  Καταλαβαίνει τα
σύμβολα συν και πλην:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003.14'
   >>> '3.14159265359'.zfill(5)
   '3.14159265359'


7.1.4. Παλιά μορφοποίηση συμβολοσειράς
--------------------------------------

Ο τελεστής % (modulo) μπορεί επίσης να χρησιμοποιηθεί για τη
μορφοποίηση συμβολοσειρών. Δεδομένων των τιμών "format % values" (όπου
*format* είναι μια συμβολοσειρά), οι προδιαγραφές μετατροπής "%" σε
*format* αντικαθίστανται με μηδέν ή περισσότερα στοιχεία *τιμών*. Αυτή
η λειτουργία είναι κοινώς γνωστή ως παρεμβολή συμβολοσειρών. Για
παράδειγμα:

   >>> import math
   >>> print('The value of pi is approximately %5.3f.' % math.pi)
   The value of pi is approximately 3.142.

Περισσότερες πληροφορίες μπορείτε να βρείτε στην ενότητα printf-style
String Formatting.


7.2. Ανάγνωση και Εγγραφή Αρχείων
=================================

Η "open()" επιστρέφει ένα *file object*, και χρησιμοποιείται πιο συχνά
με δύο ορίσματα θέσης και ένα όρισμα λέξης-κλειδιού: "open(filename,
mode, encoding=None)"

   >>> f = open('workfile', 'w', encoding="utf-8")

Το πρώτο όρισμα είναι μια συμβολοσειρά που περιέχει το όνομα αρχείου.
Το δεύτερο όρισμα είναι μια άλλη συμβολοσειρά που περιέχει μερικούς
χαρακτήρες που περιγράφουν τον τρόπο με τον οποίο θα χρησιμοποιηθεί το
αρχείο. Η *mode* μπορεί να είναι "'r'" όταν το αρχείο θα είναι μόνο
για διάβασμα, "'w'" μόνο για εγγραφή (ένα υπάρχον αρχείο με το ίδιο
όνομα θα διαγραφεί) και το "'a'" ανοίγει το αρχείο για προσάρτηση·
οποιαδήποτε δεδομένα γράφονται στο αρχείο και προστίθενται αυτόματα
στο τέλος. Το "'r+'" ανοίγει το αρχείο τόσο για ανάγνωση όσο και για
γραφή. Το όρισμα *mode* είναι προαιρετικό· το "'r'" θα θεωρείται εάν
παραληφθεί.

Κανονικά, τα αρχεία ανοίγουν σε *text mode*, που σημαίνει ότι
διαβάζετε και γράφετε συμβολοσειρές από και προς το αρχείο, οι οποίες
κωδικοποιούνται σε μια συγκεκριμένη *κωδικοποίηση*. Εάν δεν έχει
καθοριστεί η *κωδικοποιήση*, η προεπιλογή είναι εξαρτώμενη από την
πλατφόρμα (δείτε "open()"). Επειδή το UTF-8 είναι το σύγχρονο de-facto
standard, "encoding="utf-8"" συνίσταται εκτός εάν γνωρίζετε ότι πρέπει
να χρησιμοποιήσετε διαφορετική κωδικοποίηση. Η προσθήκη ενός "'b'" στη
λειτουργία ανοίγει το αρχείο σε *binary mode*. Τα δεδομένα δυαδικής
λειτουργίας διαβάζονται και γράφονται ως αντικείμενα "bytes". Δεν
μπορείτε να καθορίσετε *κωδικοποίηση* όταν ανοίγετε αρχείο σε δυαδική
λειτουργία.

Στη λειτουργία κειμένου, η προεπιλογή (default) κατά την ανάγνωση
είναι να μετατρέψετε τις καταλήξεις γραμμών για συγκεκριμένη πλατφόρμα
("\n" στο Unix, "\r\n" στα Windows) σε μόνο "\n".  Όταν γράφετε σε
λειτουργία κειμένου, η προεπιλογή είναι να μετατρέπονται οι εμφανίσεις
"\n" σε καταλήξεις γραμμών για συγκεκριμένη πλατφόρμα. Αυτή η
παρασκηνιακή τροποποίηση στα δεδομένα αρχείων είναι καλή για αρχεία
κειμένου, αλλά θα καταστρέψει δυαδικά δεδομένα όπως αυτό σε αρχεία
"JPEG" ή "EXE".  Να είστε πολύ προσεκτικοί να χρησιμοποιείτε τη
δυαδική λειτουργία όταν διαβάζετε και γράφετε τέτοια αρχεία.

Είναι καλή πρακτική να χρησιμοποιείτε το keyword "with" όταν
ασχολούμαστε με αντικείμενα αρχείου.  Το πλεονέκτημα είναι ότι το
αρχείο κλείνει σωστά μετά την ολοκλήρωση της εκτέλεσής του, ακόμα κι
αν κάποια στιγμή προκύψει εξαίρεση.  Χρησιμοποιώντας το "with" είναι
επίσης πολύ πιο σύντομο από την σύνταξη ισοδύναμου "try"-"finally"
blocks:

   >>> with open('workfile', encoding="utf-8") as f:
   ...     read_data = f.read()

   >>> # We can check that the file has been automatically closed.
   >>> f.closed
   True

Εάν δεν χρησιμοποιείτε τη keyword "with", τότε θα πρέπει να καλείτε τη
"f.close()" για να κλείσετε το αρχείο και να ελευθερώσετε αμέσως τυχόν
πόρους συστήματος που χρησιμοποιούνται από αυτό.

Προειδοποίηση:

  Η κλήση του "f.write()" χωρίς τη χρήση της keyword "with" ή η κλήση
  του "f.close()" **μπορεί** να οδηγήσει στα ορίσματα του "f.write()"
  να μην εγγραφεί πλήρως στο δίσκο, ακόμα και αν το πρόγραμμα εξέλθει
  με επιτυχία.

Μετά το κλείσιμο ενός αντικειμένου αρχείου, είτε μια δήλωση "with"
είτε καλώντας "f.close()", οι προσπάθειες χρήσης του αντικειμένου
αρχείου θα αποτύχουν αυτόματα.

   >>> f.close()
   >>> f.read()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ValueError: I/O operation on closed file.


7.2.1. Μέθοδοι Αντικειμένων Αρχείων
-----------------------------------

Τα υπόλοιπα παραδείγματα σε αυτήν την ενότητα θα υποθέσουν ότι ένα
αντικείμενο αρχείου που ονομάζεται "f" έχει ήδη δημιουργηθεί.

Για να διαβάσετε τα περιεχόμενα ενός αρχείου, καλέστε το
"f.read(size)", το οποίο διαβάζει κάποια ποσότητα δεδομένων και την
επιστρέφει ως συμβολοσειρά (σε λειτουργία κειμένου) ή ως αντικείμενο
bytes (σε δυαδική λειτουργία). Το *size* είναι προαιρετικό αριθμητικό
όρισμα.  Όταν το *size* παραλείπεται ή είναι αρνητικό, ολόκληρο το
περιεχόμενο του αρχείου θα διαβαστεί και θα επιστραφεί∙ είναι δικό σας
πρόβλημα εάν το αρχείο είναι διπλάσιο από τη μνήμη του υπολογιστή σας.
Διαφορετικά, διαβάζονται και επιστρέφονται το πολύ *size* χαρακτήρες
(σε λειτουργία κειμένου) ή byte *size* (σε δυαδική λειτουργία). Εάν
έχει φτάσει το τέλος του αρχείου, το "f.read()" θα επιστρέψει μια κενή
συμβολοσειρά ("''").

   >>> f.read()
   'This is the entire file.\n'
   >>> f.read()
   ''

Το "f.readline()" διαβάζει μία γραμμή από το αρχείο· ένας χαρακτήρας
νέας γραμμής ("\n") παραμένει στο τέλος της συμβολοσειράς, και
παραλείπεται μόνο στην τελευταία γραμμή του αρχείου εάν το αρχείο δεν
τελειώνει σε μια νέα γραμμή.  Αυτό καθιστά την τιμή επιστροφής σαφή·
εάν το "f.readline()" επιστρέφει μια κενή συμβολοσειρά, έχει φτάσει
στο τέλος του αρχείου, ενώ μια κενή γραμμή αντιπροσωπεύεται από
"'\n'", μια συμβολοσειρά που περιέχει μόνο μία νέα γραμμή.

   >>> f.readline()
   'This is the first line of the file.\n'
   >>> f.readline()
   'Second line of the file\n'
   >>> f.readline()
   ''

Για την ανάγνωση γραμμών από ένα αρχείο, μπορείτε να κάνετε loop πάνω
από το αντικείμενο του αρχείου. Αυτό είναι αποδοτικό στη μνήμη,
γρήγορο και οδηγεί σε απλό κώδικα:

   >>> for line in f:
   ...     print(line, end='')
   ...
   This is the first line of the file.
   Second line of the file

Εάν θέλετε να διαβάσετε όλες τις γραμμές ενός αρχείου σε μια λίστα,
μπορείτε επίσης να χρησιμοποιήσετε το "list(f)" ή "f.readlines()".

Το "f.write(string)" γράφει τα περιεχόμενα του *string* στο αρχείο,
επιστρέφοντας τον αριθμό των χαρακτήρων που γράφτηκαν.

   >>> f.write('This is a test\n')
   15

Άλλοι τύποι αντικειμένων πρέπει να μετατραπούν -- είτε σε μια
συμβολοσειρά (σε λειτουργία κειμένου) ή σε ένα αντικείμενο bytes (σε
δυαδική λειτουργία) -- πριν τα γράψετε:

   >>> value = ('the answer', 42)
   >>> s = str(value)  # convert the tuple to string
   >>> f.write(s)
   18

Το "f.tell()" επιστρέφει έναν ακέραιο που δίνει την τρέχουσα θέση του
αντικειμένου αρχείου στο αρχείο που αντιπροσωπεύεται ως αριθμό byte
από την αρχή του αρχείου όταν βρίσκεται σε δυαδική λειτουργία και έναν
αδιαφανή αριθμό όταν βρίσκεται σε λειτουργία κειμένου.

Για να αλλάξετε τη θέση του αντικειμένου, χρησιμοποιήστε το
"f.seek(offset, whence)".  Η θέση υπολογίζεται από την προσθήκη
*offset* σε ένα σημείο αναφοράς· το σημείο αναφοράς επιλέγεται από το
όρισμα *whence*.  Μια 0 τιμή *whence* μετρά από την αρχή του αρχείο το
1 χρησιμοποιεί την τρέχουσα θέση αρχείου και το 2 χρησιμοποιεί το
τέλος του αρχείου ως σημείο αναφοράς.  Το *whence* μπορεί να
παραληφθεί και να οριστεί από προεπιλογή 0, χρησιμοποιώντας την αρχή
του αρχείου ως σημείο αναφοράς.

   >>> f = open('workfile', 'rb+')
   >>> f.write(b'0123456789abcdef')
   16
   >>> f.seek(5)      # Go to the 6th byte in the file
   5
   >>> f.read(1)
   b'5'
   >>> f.seek(-3, 2)  # Go to the 3rd byte before the end
   13
   >>> f.read(1)
   b'd'

Σε αρχεία κειμένου (αυτά που ανοίγουν χωρίς "b" στη λειτουργία
string), επιτρέπονται μόνο αναζητήσεις σε σχέση με την αρχή του
αρχείου (η εξαίρεση είναι η αναζήτηση μέχρι το ίδιο το αρχείο που
τελειώνει με "seek(0, 2)") και οι μόνες έγκυρες τιμές *offset* είναι
αυτές που επιστρέφονται από το "f.tell()", ή μηδέν. Οποιαδήποτε άλλη
τιμή *offset* παράγει  απροσδιόριστη συμπεριφορά.

Τα αντικείμενα αρχείου έχουν ορισμένες πρόσθετες μεθόδους, όπως
"isatty()" και "truncate()" που χρησιμοποιούνται λιγότερο συχνά·
συμβουλευτείτε την Αναφορά της Βιβλιοθήκης για έναν πλήρη οδηγό για τα
αντικείμενα αρχείων.


7.2.2. Αποθήκευση δομημένων δεδομένων με "json"
-----------------------------------------------

Οι συμβολοσειρές μπορούν εύκολα να γραφούν και να διαβαστούν από ένα
αρχείο.  Οι αριθμοί απαιτούν λίγο περισσότερη προσπάθεια, καθώς η
μέθοδος "read()" επιστρέφει μόνο συμβολοσειρές, οι οποίες θα πρέπει να
περάσουν μια συνάρτηση όπως "int()", που παίρνει μια συμβολοσειρά όπως
"'123'" και επιστρέφει την αριθμητική της τιμή 123.  Όταν θέλετε να
αποθηκεύσετε πιο σύνθετους τύπους δεδομένων, όπως ένθετε λίστες και
λεξικά, η ανάλυση και η σειριοποίηση με το χέρι γίνεται περίπλοκη.

Αντί να χρειάζεται οι χρήστες να γράφουν και να διορθώνουν συνεχώς
κώδικα για να αποθηκεύουν πολύπλοκους τύπους δεδομένων σε αρχεία, η
Python σας επιτρέπει να χρησιμοποιείτε τη δημοφιλή μορφή ανταλλαγής
δεδομένων που ονομάζεται JSON (JavaScript Object Notation).  Το
standard module που ονομάζεται "json" μπορεί να λάβει ιεραρχίες
δεδομένων Python, και να τις μετατρέψει σε αναπαραστάσεις
συμβολοσειρών· αυτή η διαδικασία ονομάζεται *serializing*..  Η
ανασύνθεση των δεδομένων από την αναπαράσταση συμβολοσειράς ονομάζεται
*deserializing*.  Μεταξύ σειριοποίησης και αποσειριοποίησης, η
συμβολοσειρά που αντιπροσωπεύει το αντικείμενο μπορεί να έχει
αποθηκευτεί σε ένα αρχείο ή δεδομένα ή να έχει σταλεί μέσω μιας
σύνδεσης δικτύου σε κάποιο απομακρυσμένο μηχάνημα.

Σημείωση:

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

Εάν έχετε ένα αντικείμενο "x", μπορείτε να δείτε την αναπαράσταση
συμβολοσειράς JSON με μια απλή γραμμή κώδικα:

   >>> import json
   >>> x = [1, 'simple', 'list']
   >>> json.dumps(x)
   '[1, "simple", "list"]'

Μια άλλη παραλλαγή της συνάρτησης "dumps()", που ονομάζεται "dump()",
απλώς σειριοποιεί το αντικείμενο σε ένα *text file*.  Έτσι, εάν το "f"
είναι ένα αντικείμενο *text file* που ανοίγει για εγγραφή, μπορούμε να
κάνουμε αυτό:

   json.dump(x, f)

Για να αποκωδικοποιήσετε ξανά το αντικείμενο, εάν το "f" είναι ένα
αντικείμενο *binary file* ή *text file* που έχει ανοίξει για ανάγνωση:

   x = json.load(f)

Σημείωση:

  Τα αρχεία JSON πρέπει να είναι κωδικοποιημένα σε UTF-8.
  Χρησιμοποιήστε το "encoding="utf-8"" όταν ανοίγετε το αρχείο JSON ως
  *text file* τόσο για ανάγνωση όσο και για εγγραφή.

Αυτή η απλή τεχνική σειριοποίησης μπορεί να χειριστεί λίστες και
λεξικά, άλλα η σειριοποίηση αυθαίρετων στιγμιοτύπων κλάσεων σε JSON
απαιτεί λίγη επιπλέον προσπάθεια. Η αναφορά για το module "json"
περιέχει μια εξήγηση για αυτό.

Δείτε επίσης:

  "pickle" - το pickle module

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