python file io lettura scrittura

Input/Output su File in Python

Obiettivi della pillola

In questa guida imparerai a:

  • aprire e chiudere i file in sicurezza con with;
  • leggere e scrivere file di testo (.txt, .csv, ecc.);
  • iterare sulle righe di un file in modo efficiente;
  • gestire file binari (immagini, dati grezzi);
  • spostarti all’interno di un file con seek() e tell().

Introduzione: Aprire i File

Per lavorare con un file, bisogna prima aprirlo con la funzione open(nome_file, modalità).

Le Modalità di Apertura

Le più importanti sono:

  • 'r' (Read): Lettura (default). Errore se il file non esiste.
  • 'w' (Write): Scrittura. Sovrascrive il file se esiste, lo crea se non esiste.
  • 'a' (Append): Scrittura in coda. Aggiunge dati alla fine senza cancellare quelli esistenti.
  • 'b' (Binary): Da aggiungere alle precedenti per file binari (es. 'rb', 'wb').

Il Context Manager with

È fondamentale chiudere sempre i file per liberare risorse. In Python, il modo migliore è usare il blocco with, che chiude il file automaticamente anche in caso di errori.

# NON FARE COSÌ (rischio se c'è un errore prima di close)
f = open("dati.txt", "r")
content = f.read()
f.close()

# FAI COSÌ (sicuro e pythonico)
with open("dati.txt", "r") as f:
    content = f.read()
# Qui il file è già chiuso

File di Testo

Lettura

Ci sono diversi metodi per leggere il contenuto:

  1. read(): Legge tutto il file in un’unica stringa.
  2. readline(): Legge una sola riga alla volta (incluso il \n finale).
  3. readlines(): Legge tutte le righe e restituisce una lista di stringhe.
  4. Iterazione diretta: Il metodo più efficiente per file grandi.
# Metodo migliore per scorrere un file riga per riga
with open("elenco.txt", "r") as f:
    for riga in f:
        # riga contiene anche il carattere "a capo"
        print(riga.strip()) # strip() rimuove spazi e \n inizio/fine

Cercare righe specifiche con il ciclo for:

# Il ciclo for è il modo standard per leggere una riga alla volta.
# Non carica tutto il file in memoria!
with open("log.txt", "r") as f:
    for riga in f:
        if riga.startswith("ERROR:"):
            print("Trovato un errore:", riga.strip())

Scrittura

with open("output.txt", "w") as f:
    f.write("Prima riga\n")   # Bisogna mettere \n esplicitamente
    f.write("Seconda riga\n")
    
    # Scrivere una lista di righe
    righe = ["Terza\n", "Quarta\n"]
    f.writelines(righe)

Esempio: Contare le righe

conteggio = 0
with open("log.txt", "r") as f:
    for riga in f:
        conteggio += 1
print(f"Il file ha {conteggio} righe.")

File Binari

I file binari (immagini, audio, eseguibili) non sono divisi in righe e non usano codifiche come UTF-8. Si leggono e scrivono come sequenze di byte.

# Copiare un'immagine (byte per byte)
with open("foto.jpg", "rb") as source:      # rb = read binary
    dati = source.read()                    # Legge tutti i byte

with open("copia_foto.jpg", "wb") as dest:  # wb = write binary
    dest.write(dati)

Lettura a blocchi (chunk) con ciclo while — utile per file di grandi dimensioni:

# Per file grandi, si legge "a pezzi" (chunk) fino alla fine
CHUNK_SIZE = 4096  # Leggiamo 4 KB alla volta

with open("video.mp4", "rb") as f:
    while True:
        dati = f.read(CHUNK_SIZE)
        if not dati:  # Se la lettura restituisce bytes vuoti, è finito
            break
        # Elabora i dati (es. salvali, inviali, ecc.)
        pass

Nota: Quando leggi in modalità binaria, ottieni oggetti di tipo bytes (es. b'\x89PNG...'), non stringhe.

Spostarsi nel File: seek() e tell()

Il file ha un “cursore” che indica la posizione corrente (in byte). Quando leggi, il cursore avanza.

  • tell(): Restituisce la posizione attuale del cursore (intero).
  • seek(offset, whence): Sposta il cursore.
    • offset: Di quanti byte spostarsi.
    • whence: Da dove iniziare (0: inizio file, 1: posizione corrente, 2: fine file).

Nota: Nei file di testo, seek() è supportato solo parzialmente (solitamente solo all’inizio seek(0) o a posizioni ottenute da tell()). Nei file binari è pienamente supportato.

with open("dati.bin", "rb") as f:
    print(f.read(10))   # Legge i primi 10 byte
    print(f.tell())     # Stampa 10
    
    f.seek(0)           # Torna all'inizio
    print(f.read(5))    # Rilegge i primi 5 byte
    
    f.seek(0, 2)            # Va alla fine del file
    dimensione = f.tell()   # La posizione alla fine è la dimensione!
    print(f"Il file è grande {dimensione} byte")

Esercitazione Pratica

Esercizio 1 — Analisi File Log

Hai un file di testo server.log.

  1. Conta quante righe contengono la parola “ERROR”.
  2. Crea un nuovo file errors_only.txt copiandoci dentro solo quelle righe.

Esercizio 2 — Messaggio Nascosto

Hai un file binario. Sai che il messaggio segreto inizia al byte 50 e finisce al byte 100. Scrivi uno script che:

  1. Apre il file in modalità binaria.
  2. Si sposta al byte 50.
  3. Legge 50 byte.
  4. Prova a decodificarli come testo (dati.decode('utf-8')) e lo stampa.