Skip to content

7. File di testo

Image

7.1. Script [fic_01]: Lettura/scrittura di un file di testo

Lo script seguente illustra un esempio di utilizzo dei file di testo:

#  imports
import sys


#  creation and sequential processing of a text file
#  this is a set of lines of the form login:pwd:uid:gid:infos:dir:shell
#  each line is put into a dictionary in the form login => uid:gid:infos:dir:shell

# --------------------------------------------------------------------------
def affiche_infos(dico: dict, clé: str):
    #  displays the value associated with key in the dico dictionary if it exists
    if clé in dico.keys():
        #  displays the value associated with the key
        print(f"{clé} : {dico[clé]}")
    else:
        #  key is not a dictionary key dico
        print(f"la clé [{clé}] n'existe pas")


#  main -----------------------------------------------
#  set the file name
FILE_NAME = "./data/infos.txt"

#  creating and filling text files
fic = None
try:
    #  open file for writing (w=write)
    fic = open(FILE_NAME, "w")
    #  generate arbitrary content
    for i in range(1, 101):
        #  a line
        ligne = f"login{i}:pwd{i}:uid{i}:gid{i}:infos{i}:dir{i}:shell{i}"
        #  is written to the text file
        fic.write(f"{ligne}\n")
except IOError as erreur:
    print(f"Erreur d'exploitation du fichier {FILE_NAME} : {erreur}")
    sys.exit()
finally:
    #  close the file if it has been opened
    if fic:
        fic.close()

#  open it for reading
fic = None
try:
    #  open file for reading
    fic = open(FILE_NAME, "r")
    #  empty dictionary at start
    dico = {}
    #  each line is put into the [dico] dictionary as login => uid:gid:infos:dir:shell
    #  read 1st line, removing leading and trailing spaces
    ligne = fic.readline().strip()
    #  as long as the line is not empty
    while ligne != '':
        #  put the line in a table
        infos = ligne.split(":")
        #  retrieve login
        login = infos[0]
        #  we neglect the pwd
        infos[0:2] = []
        #  create a dictionary entry
        dico[login] = infos
        #  read next line
        ligne = fic.readline().strip()
except IOError as erreur:
    print(f"Erreur d'exploitation du fichier {FILE_NAME} : {erreur}")
    sys.exit()
finally:
    #  close the file if it has been opened
    if fic:
        fic.close()

#  using the dico dictionary
affiche_infos(dico, "login10")
affiche_infos(dico, "X")

Note:

  • riga 28: apre il file in scrittura (w=write). Se il file esiste già, verrà sovrascritto;
  • Righe 30–34: generano 100 righe nel file di testo;
  • riga 34: per scrivere una riga nel file di testo. Il metodo [write] non aggiunge un carattere di nuova riga. Pertanto, è necessario includerlo nel testo scritto;
  • righe 35–37: gestiscono eventuali eccezioni;
  • riga 37: interrompe l'esecuzione dello script (tuttavia, dopo che il blocco finally è stato eseguito);
  • righe 38–41: in tutti i casi, indipendentemente dal verificarsi o meno di un errore, chiudere il file se è aperto;
  • riga 47: aprire il file in lettura (r=read);
  • riga 49: definizione di un dizionario vuoto;
  • riga 52: il metodo [readline] legge una riga di testo, compreso il carattere di fine riga. Il metodo [strip] rimuove gli "spazi" dall'inizio e dalla fine della stringa. Per "spazio" si intendono i caratteri di spaziatura, le interruzioni di riga, le interruzioni di pagina, i tabulatori e alcuni altri. Quindi, in questo caso, [line] non conterrà i caratteri di interruzione di riga [\r\n] (Windows) o [\n] (Unix);
  • riga 54: il file viene elaborato fino a quando non si incontra una riga vuota;
  • righe 54–64: il file di testo viene trasferito al dizionario [dico]. La chiave è il campo [login] e il valore è costituito dai campi [uid:gid:infos:dir:shell];
  • righe 65–67: gestiscono eventuali eccezioni;
  • righe 68–71: chiudere il file in ogni caso, indipendentemente dal verificarsi o meno di un errore;
  • righe 74-75: interrogare il dizionario [dico];

Il file [data/infos.txt]:

1
2
3
4
5
6
login0:pwd0:uid0:gid0:infos0:dir0:shell0
login1:pwd1:uid1:gid1:infos1:dir1:shell1
login2:pwd2:uid2:gid2:infos2:dir2:shell2
login98:pwd98:uid98:gid98:infos98:dir98:shell98
login99:pwd99:uid99:gid99:infos99:dir99:shell99

Output dello schermo:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fichiers/fic_01.py
login10 : ['uid10', 'gid10', 'infos10', 'dir10', 'shell10']
la clé [X] n'existe pas
 
Process finished with exit code 0

7.2. Script [fic_02]: Gestione dei file di testo codificati in UTF-8

Nel resto di questo documento, lavoreremo esclusivamente con file di testo codificati in UTF-8. Per prima cosa, configureremo PyCharm:

Image

  • in [5-6]: selezionare la codifica UTF-8 per i file di progetto;

Per creare un file codificato in UTF-8, procedere come segue (fic-02):

1
2
3
4
5
6
7
8
#  imports
import codecs

#  writing utf8 to a text file
#  exceptions are not handled
file=codecs.open("./data/utf8.txt","w","utf8")
file.write("Hélène est partie à Bâle pendant l'été chez sa grand-mère")
file.close()

Note

  • riga 2: per gestire la codifica del file, importiamo il modulo [codecs];
  • riga 6: il metodo [codecs.open] viene utilizzato come la funzione standard [open]. Tuttavia, è possibile specificare la codifica desiderata (durante la creazione) o quella esistente (durante la lettura). Dopo l'apertura, l'oggetto [file] ottenuto alla riga 6 viene utilizzato come un file standard;
  • riga 7: sono stati utilizzati caratteri accentati, che solitamente hanno rappresentazioni diverse a seconda della codifica dei caratteri utilizzata;

Risultati

All'apertura del file [data/utf8.txt] ottenuto (vedi riga 6), si ottiene il seguente risultato:

Image

7.3. Script [fic_03]: gestione di file di testo codificati in ISO-8859-1

Lo script [fic_03] fa la stessa cosa dello script [fic_02], ma codifica il file di testo in ISO-8859-1. Vogliamo mostrare la differenza tra i file risultanti:

1
2
3
4
5
6
7
8
#  imports
import codecs

#  iso-8859-1 writing in a text file
#  exceptions are not handled
file=codecs.open("./data/iso-8859-1.txt","w","iso-8859-1")
file.write("Hélène est partie à Bâle pendant l'été chez sa grand-mère")
file.close()

Quando apriamo il file [data/iso-8859-1] creato alla riga 6, otteniamo il seguente risultato:

Image

Poiché abbiamo configurato il progetto per lavorare con file UTF-8, PyCharm ha tentato di aprire il file [iso-8859-1.txt] in UTF-8. Come si può vedere [1], il file non è in UTF-8. Suggerisce quindi [2] di ricaricare il file con una codifica diversa:

Image

  • in [3-5]: il file viene ricaricato utilizzando la codifica ISO-8859-1;

Image

  • in [6], lo stesso file ma visualizzato con una codifica diversa;

Se torniamo alle impostazioni del progetto:

Image

  • vediamo che in [6-7], PyCharm ha indicato che il file [iso-8859-1.txt] dovrebbe essere aperto con la codifica ISO-8859-1. Si tratta quindi di un'eccezione alla regola [5];

7.4. Script [json_01]: Lavorare con un file JSON

JSON sta per JavaScript Object Notation. Come suggerisce il nome, si tratta di una rappresentazione testuale degli oggetti JavaScript. In questo caso, lo useremo con oggetti Python.

Il file JSON gestito [data/in.json] avrà questo aspetto:

Image

  • In [2], possiamo vedere che il contenuto testuale del file [in.json] potrebbe rappresentare un dizionario Python. PyCharm ha formattato (Ctrl-Alt-L) questo testo, ma anche se fosse su una singola riga, non farebbe alcuna differenza. Il formato del testo è irrilevante purché rappresenti sintatticamente un oggetto Python;

Lo script [json-01] mostra come utilizzare questo file:

#  imports
import codecs
import json
import sys

#  read/write file jSON
inFile=None
outFile=None
try:
    #  open file jSON in read mode
    inFile = codecs.open("./data/in.json", "r", "utf8")
    #  transfer content to a dictionary
    data = json.load(inFile)
    #  display of read data
    print(f"data={data}, type(data)={type(data)}")
    limites = data['limites']
    print(f"limites={limites}, type(limites)={type(limites)}")
    print(f"limites[1]={limites[1]}, type(limites[1])={type(limites[1])}")
    #  transfer the [data] dictionary to a json file
    outFile = codecs.open("./data/out.json", "w", "utf8")
    json.dump(data, outFile)
except BaseException as erreur:
    #  display error and exit
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    #  close files if they are open
    if inFile:
        inFile.close()
    if outFile:
        outFile.close()

Note

  • Riga 3: per lavorare con JSON, importiamo il modulo [json];
  • riga 11: lavoreremo con file JSON codificati in UTF-8. Qui, apriamo il file [data/in.json] utilizzando il modulo [codecs];
  • riga 13: il metodo [json.load] legge il contenuto del file JSON e lo memorizza nella variabile [data]. Il tipo di questa variabile sarà un dizionario;
  • righe 15–18: per verificare di aver effettivamente ottenuto un dizionario Python, visualizziamo alcuni dei suoi elementi;
  • righe 20–21: eseguiamo l'operazione inversa: il dizionario [data] viene scritto in un file codificato in UTF-8 utilizzando il metodo [json.dump];
  • righe 22–25: gestione delle eventuali eccezioni;
  • righe 26-31: in ogni caso, indipendentemente dal verificarsi o meno di un errore, chiudiamo tutti i file che potrebbero essere stati aperti;

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fichiers/json_01.py
data={'limites': [9964, 27519, 73779, 156244, 0], 'coeffR': [0, 0.14, 0.3, 0.41, 0.45], 'coeffN': [0, 1394.96, 5798, 13913.69, 20163.45], 'PLAFOND_QF_DEMI_PART': 1551, 'PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION': 21037, 'PLAFOND_REVENUS_COUPLE_POUR_REDUCTION': 42074, 'VALEUR_REDUC_DEMI_PART': 3797, 'PLAFOND_DECOTE_CELIBATAIRE': 1196, 'PLAFOND_DECOTE_COUPLE': 1970, 'PLAFOND_IMPOT_COUPLE_POUR_DECOTE': 2627, 'PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE': 1595, 'ABATTEMENT_DIXPOURCENT_MAX': 12502, 'ABATTEMENT_DIXPOURCENT_MIN': 437}, type(data)=<class 'dict'>
limites=[9964, 27519, 73779, 156244, 0], type(limites)=<class 'list'>
limites[1]=27519, type(limites[1])=<class 'int'>
 
Process finished with exit code 0
  • Le righe 2–4 mostrano che abbiamo recuperato con successo il dizionario dal file JSON;

Ora, diamo un'occhiata al contenuto del file [data/out.json]:

Image

Il testo nel file è su una singola riga. Tuttavia, PyCharm riconosce i file JSON e possiamo formattarli — proprio come i file Python e altri — utilizzando Ctrl-Alt-L. Questo ci dà il seguente risultato:

Image

7.5. Script [json_02]: Gestione dei file JSON codificati in UTF-8

Un file JSON codificato in UTF-8 può assumere due forme:

#  imports
import codecs
import json
import sys

#  dictionary
data = {'marié': 'oui', 'impôt': 1340}

#  write a jSON file
out_file1 = None
out_file2 = None
try:
    #  transfer the [data] dictionary to a json file
    out_file1 = codecs.open("./data/out1.json", "w", "utf8")
    json.dump(data, out_file1, ensure_ascii=True)
    #  transfer the [data] dictionary to a json file
    out_file2 = codecs.open("./data/out2.json", "w", "utf8")
    json.dump(data, out_file2, ensure_ascii=False)
except BaseException as erreur:
    #  display error and exit
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    #  close files if they are open
    if out_file1:
        out_file1.close()
    if out_file2:
        out_file2.close()

  • In questo script, il dizionario [data] (riga 7) viene scritto in due file JSON (righe 14, 17);
  • righe 14, 17: in entrambi i casi, viene creato un file di testo UTF-8;
  • riga 15: durante la scrittura del dizionario, utilizziamo il parametro denominato [ensure_ascii=True];
  • riga 18: durante la scrittura del dizionario, utilizziamo il parametro denominato [ensure_ascii=False];

Ecco i due file risultanti:

Image

  • Nel file [out1.json], i caratteri accentati sono stati sostituiti da una sequenza di caratteri che ne rappresenta il codice UTF-8. Questo processo viene talvolta definito "escaping". Tecnicamente, nel formato binario di [out1.json], il carattere é in [marié] è rappresentato dai codici binari UTF-8 dei 6 caratteri [\u00e9] in successione;
  • Nel file [out2.json], i caratteri accentati sono stati lasciati così come sono. Ciò significa che nei dati binari di [out2.json], questi caratteri sono rappresentati dal loro codice binario UTF-8 (un solo codice UTF-8, anziché 6 come in [out1]). Per il carattere é in [marié], troviamo quindi il codice binario a 4 byte [00e9];
  • è il valore del parametro [ensure_ascii] del metodo [json.dump] che determina il formato utilizzato;

Alcune applicazioni utilizzano l'UTF-8 "escaped" per i propri file JSON. In tal caso, deve essere utilizzato il valore [ensure_ascii=True]. Questo valore è in realtà quello predefinito. Pertanto, se il parametro [ensure_ascii] non viene utilizzato, lavoreremo con file JSON UTF-8 escaped.

Lo script prosegue come segue:

#  imports
import codecs
import json
import sys

#  dictionary
data = {'marié': 'oui', 'impôt': 1340}



#  read back files jSON
in_file1 = None
in_file2 = None
try:
    #  transfer file jSON 1 to a dictionary
    in_file1 = codecs.open("./data/out1.json", "r", "utf8")
    dico1 = json.load(in_file1)
    #  display
    print(f"dico1={dico1}")
    #  transfer the jSON 2 file to a dictionary
    in_file2 = codecs.open("./data/out2.json", "r", "utf8")
    dico2 = json.load(in_file2)
    #  display
    print(f"dico2={dico2}")
except BaseException as erreur:
    #  display error and exit
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    #  close files if they are open
    if in_file1:
        in_file1.close()
    if in_file2:
        in_file2.close()

Note

  • righe 11–34: leggere i due file [out1.json, out2.json] e visualizzare il dizionario letto in ciascun caso;

Risultati

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fichiers/json_02.py
dico1={'marié': 'oui', 'impôt': 1340}
dico2={'marié': 'oui', 'impôt': 1340}

Process finished with exit code 0

Sorprendentemente, vediamo che non è stato necessario specificare il tipo di codifica (con o senza escape) della stringa JSON da leggere alla funzione [json.load] (righe 17, 22). In entrambi i casi, otteniamo il dizionario corretto.