Skip to content

7. I 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:


# importazioni
import sys


# creazione e successiva elaborazione sequenziale di un file di testo
# questo è un insieme di righe della forma login:pwd:uid:gid:infos:dir:shell
# ogni riga viene inserita in un dizionario nella forma login => uid:gid:infos:dir:shell

# --------------------------------------------------------------------------
def affiche_infos(dico: dict, clé: str):
    # visualizza il valore associato alla chiave nel dizionario "dico", se esiste
    if clé in dico.keys():
        # viene visualizzato il valore associato alla chiave
        print(f"{clé} : {dico[clé]}")
    else:
        # la chiave non è presente nel dizionario "dico"
        print(f"la clé [{clé}] n'existe pas")


# main -----------------------------------------------
# si imposta il nome del file
FILE_NAME = "./data/infos.txt"

# creazione e compilazione del file di testo
fic = None
try:
    # apertura del file in modalità scrittura (w=write)
    fic = open(FILE_NAME, "w")
    # si genera un contenuto arbitrario
    for i in range(1, 101):
        # una riga
        ligne = f"login{i}:pwd{i}:uid{i}:gid{i}:infos{i}:dir{i}:shell{i}"
        # viene scritta nel file di testo
        fic.write(f"{ligne}\n")
except IOError as erreur:
    print(f"Erreur d'exploitation du fichier {FILE_NAME} : {erreur}")
    sys.exit()
finally:
    # si chiude il file se è stato aperto
    if fic:
        fic.close()

# si apre in lettura
fic = None
try:
    # apertura del file in lettura
    fic = open(FILE_NAME, "r")
    # dizionario vuoto all'inizio
    dico = {}
    # ogni riga viene inserita nel dizionario [dico] nella forma login => uid:gid:infos:dir:shell
    # lettura della prima riga rimuovendo gli spazi all'inizio e alla fine della riga
    ligne = fic.readline().strip()
    # finché la riga non è vuota
    while ligne != '':
        # si inserisce la riga in un array
        infos = ligne.split(":")
        # si recupera il login
        login = infos[0]
        # si ignora la password
        infos[0:2] = []
        # si crea una voce nel dizionario
        dico[login] = infos
        # lettura della riga successiva
        ligne = fic.readline().strip()
except IOError as erreur:
    print(f"Erreur d'exploitation du fichier {FILE_NAME} : {erreur}")
    sys.exit()
finally:
    # si chiude il file se è stato aperto
    if fic:
        fic.close()

# utilizzo del dizionario
affiche_infos(dico, "login10")
affiche_infos(dico, "X")

Note:

  • riga 28: apertura del file in modalità scrittura (w=write). Se il file esiste già, verrà sovrascritto;
  • righe 30-34: si generano 100 righe nel file di testo;
  • riga 34: per scrivere una riga nel file di testo. Il metodo [write] non aggiunge il carattere di fine riga. È quindi necessario inserirlo nel testo scritto;
  • righe 35-37: gestione dell'eventuale eccezione;
  • riga 37: interruzione dell'esecuzione dello script (tuttavia dopo l'esecuzione della clausola finally);
  • righe 38-41: in ogni caso, che si verifichi o meno un errore, si chiude il file se è aperto;
  • riga 47: apertura del 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» all’inizio e alla fine della stringa. Per «spazio» si intendono caratteri bianchi, segni di fine riga, salti di pagina, tabulazioni e alcuni altri. Quindi, in questo caso, [ligne] non conterrà i caratteri di fine riga presenti in [\r\n] (Windows) o [\n] (Unix);
  • riga 54: si elabora il file finché non si incontra una riga vuota;
  • righe 54-64: il file di testo viene trasferito nel dizionario [dico]. La chiave è il campo [login], il valore è costituito dai campi [uid:gid:infos:dir:shell];
  • righe 65-67: gestione dell'eventuale eccezione;
  • righe 68-71: chiusura del file in ogni caso, indipendentemente dalla presenza o meno di un errore;
  • righe 74-75: utilizzo del 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

Risultati sullo 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 prosieguo del documento, gestiremo file di testo codificati esclusivamente in UTF-8. Per prima cosa configureremo PyCharm:

Image

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

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


# importazioni
import codecs

# scrittura in formato UTF-8 in un file di testo
# non si gestiscono le eccezioni
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 dei file, si importa il modulo [codecs];
  • riga 6: il metodo [codecs.open] si utilizza come la classica funzione [open]. È tuttavia possibile specificare la codifica desiderata (creazione) o esistente (lettura). Dopo l’apertura, l’oggetto [file] ottenuto alla riga 6 si utilizza come un file classico;
  • riga 7: sono stati utilizzati caratteri accentati che, nella maggior parte dei casi, hanno rappresentazioni diverse a seconda del codice caratteri utilizzato;

Risultati

Quando si apre il file [data/utf8.txt] ottenuto (cfr. riga 6), si ottiene il seguente risultato:

Image

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

Lo script [fic_03] svolge la stessa funzione dello script [fic_02], ma codifica il file di testo in ISO-8859-1. Di seguito viene illustrata la differenza tra i file ottenuti:


# importazioni
import codecs

# scrittura in iso-8859-1 in un file di testo
# non vengono gestite le eccezioni
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 si apre il file [data/iso-8859-1] creato alla riga 6, si ottiene il seguente risultato:

Image

Poiché abbiamo configurato il progetto per funzionare con i file UTF-8, PyCharm ha tentato di aprire il file [iso-8859-1.txt] come UTF-8. È in grado di rilevare che il file [1] non corrisponde a UTF-8. Propone quindi a [2] di ricaricare il file con una diversa codifica:

Image

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

Image

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

Se si torna alle impostazioni del progetto:

Image

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

7.4. Script [json_01]: gestione di un file jSON

JSON indica la notazione degli oggetti JavaScript. Come suggerisce il nome, si tratta di una modalità di rappresentazione testuale degli oggetti del linguaggio JavaScript. In questo contesto la utilizzeremo con oggetti Python.

Il file jSON gestito da [data/in.json] sarà il seguente:

Image

  • in [2], si vede 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 sola riga non cambierebbe nulla. La forma del testo non ha alcuna importanza purché rappresenti sintatticamente un oggetto Python;

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


# importazioni
import codecs
import json
import sys

# lettura/scrittura di un file jSON
inFile=None
outFile=None
try:
    # apertura del file jSON in modalità di lettura
    inFile = codecs.open("./data/in.json", "r", "utf8")
    # trasferimento del contenuto in un dizionario
    data = json.load(inFile)
    # visualizzazione dei dati letti
    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])}")
    # trasferimento del dizionario [data] in un file JSON
    outFile = codecs.open("./data/out.json", "w", "utf8")
    json.dump(data, outFile)
except BaseException as erreur:
    # viene visualizzato l'errore e si esce
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    # chiusura dei file, se aperti
    if inFile:
        inFile.close()
    if outFile:
        outFile.close()

Note

  • riga 3: per gestire JSON, si importa il modulo [json];
  • riga 11: gestiremo file jSON codificati in UTF-8. Qui si apre il file [data/in.json] con il modulo [codecs];
  • riga 13: il metodo [json.load] legge il contenuto del file jSON e lo assegna alla variabile [data]. Il tipo di questa variabile sarà in questo caso un dizionario;
  • righe 15-18: per dimostrare che si è effettivamente ottenuto un dizionario Python, si visualizzano alcuni dei suoi elementi;
  • righe 20-21: si esegue l'operazione inversa: il dizionario [data] viene salvato in un file con estensione UTF-8 grazie al metodo [json.dump];
  • righe 22-25: gestione di eventuali eccezioni;
  • righe 26-31: in ogni caso, che si verifichi o meno un errore, si chiudono 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 il dizionario presente nel file jSON è stato recuperato correttamente;

Ora esaminiamo il contenuto del file [data/out.json]:

Image

Il testo del file è su un'unica riga. Tuttavia, PyCharm riconosce i file jSON ed è possibile formattarli, come i file Python e altri, tramite Ctrl-Alt-L. Si ottiene quindi quanto segue:

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:


# importazioni
import codecs
import json
import sys

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

# scrittura di un file jSON
out_file1 = None
out_file2 = None
try:
    # trasferimento del dizionario [data] in un file json
    out_file1 = codecs.open("./data/out1.json", "w", "utf8")
    json.dump(data, out_file1, ensure_ascii=True)
    # trasferimento del dizionario [data] in un file json
    out_file2 = codecs.open("./data/out2.json", "w", "utf8")
    json.dump(data, out_file2, ensure_ascii=False)
except BaseException as erreur:
    # viene visualizzato l'errore e il programma viene chiuso
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    # chiusura dei file se sono aperti
    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, si crea un file di testo UTF-8;
  • riga 15: durante la scrittura del dizionario, si utilizza il parametro denominato [ensure_ascii=True];
  • riga 18: durante la scrittura del dizionario, si utilizza il parametro denominato [ensure_ascii=False];

Ecco i due file ottenuti:

Image

  • nel file [out1.json], i caratteri accentati sono stati sostituiti da una serie di caratteri che ne rappresentano il codice UTF-8. A volte si dice che siano stati «escapati». Tecnicamente, nel file binario [out1.json], per il carattere «é» di [marié] si trovano in successione i codici binari UTF-8 dei 6 caratteri [\u00e9];
  • nel file [out2.json], i caratteri accentati sono stati lasciati così come sono. Ciò significa che nel file binario di [out2.json] questi caratteri sono rappresentati dal loro codice binario UTF-8 (un solo codice UTF-8 invece di 6 per out1). Per il carattere «é» di [marié], si troverà quindi il codice binario [00e9] su 4 byte;
  • è il valore del parametro [ensure_ascii] del metodo [json.dump] a determinare il formato utilizzato;

Alcune applicazioni utilizzano UTF-8 «escapato» per i propri file jSON. In tal caso, occorre utilizzare il valore [ensure_ascii=True]. Questo valore è, infatti, quello predefinito. Se quindi non si utilizza il parametro [ensure_ascii], si lavorerà con file jSON e UTF-8 "escapati".

Lo script prosegue come segue:


# importazioni
import codecs
import json
import sys

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



# rilettura dei file jSON
in_file1 = None
in_file2 = None
try:
    # trasferimento del file jSON 1 in un dizionario
    in_file1 = codecs.open("./data/out1.json", "r", "utf8")
    dico1 = json.load(in_file1)
    # visualizzazione
    print(f"dico1={dico1}")
    # trasferimento del file jSON 2 in un dizionario
    in_file2 = codecs.open("./data/out2.json", "r", "utf8")
    dico2 = json.load(in_file2)
    # visualizzazione
    print(f"dico2={dico2}")
except BaseException as erreur:
    # viene visualizzato l'errore e si esce
    print(f"L'erreur suivante s'est produite : {erreur}")
    sys.exit()
finally:
    # chiusura dei file se sono aperti
    if in_file1:
        in_file1.close()
    if in_file2:
        in_file2.close()

Note

  • righe 11-34: rilettura dei due file [out1.json, out2.json] e visualizzazione del dizionario letto in ciascuno dei casi;

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, si nota che non è stato necessario specificare alla funzione [json.load] (righe 17, 22) il tipo di codifica (con o senza escape) della stringa jSON da leggere. In entrambi i casi si ottiene il dizionario corretto.