00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

File I/O in Lua

Lua fornisce due approcci per lavorare con i file: il modello semplice tramite la libreria io e il modello completo tramite handle di file espliciti. In questa guida esploreremo entrambi, con esempi pratici per scenari reali.

Apertura dei File con io.open

La funzione io.open() apre un file e restituisce un handle (oggetto file) che permette di leggere o scrivere. Se l’operazione fallisce, restituisce nil seguito da un messaggio di errore.

local file, errore = io.open("dati.txt", "r")
if not file then
    print("Errore nell'apertura: " .. errore)
else
    -- operazioni sul file
    file:close()
end

Modalita’ di Apertura

Modalita’ Descrizione
"r" Lettura (default). Il file deve esistere.
"w" Scrittura. Crea il file o sovrascrive il contenuto.
"a" Append. Aggiunge alla fine del file esistente.
"rb" Lettura binaria.
"wb" Scrittura binaria.
"ab" Append binario.
"r+" Lettura e scrittura. Il file deve esistere.
"w+" Lettura e scrittura. Crea o sovrascrive.

Lettura dei File

Lua offre diversi formati di lettura tramite il metodo file:read().

Leggere l’Intero File

local file = io.open("poesia.txt", "r")
if file then
    local contenuto = file:read("*a")  -- legge tutto il contenuto
    print(contenuto)
    file:close()
end

Leggere Riga per Riga

local file = io.open("lista.txt", "r")
if file then
    local riga = file:read("*l")  -- legge una riga (senza newline)
    while riga do
        print(riga)
        riga = file:read("*l")
    end
    file:close()
end

Leggere un Numero

local file = io.open("numeri.txt", "r")
if file then
    local numero = file:read("*n")  -- legge un numero
    while numero do
        print("Numero letto: " .. numero)
        numero = file:read("*n")
    end
    file:close()
end

Leggere un Numero Specifico di Byte

local file = io.open("dati.bin", "rb")
if file then
    local blocco = file:read(1024)  -- legge 1024 byte
    while blocco do
        print("Letti " .. #blocco .. " byte")
        blocco = file:read(1024)
    end
    file:close()
end

Scrittura dei File

Il metodo file:write() scrive stringhe nel file. Non aggiunge automaticamente un carattere di nuova riga.

local file = io.open("output.txt", "w")
if file then
    file:write("Prima riga\n")
    file:write("Seconda riga\n")
    file:write("Valore: " .. tostring(42) .. "\n")
    file:close()
    print("File scritto con successo!")
end

Scrittura con Append

-- Aggiunge contenuto senza cancellare quello esistente
local file = io.open("log.txt", "a")
if file then
    file:write(os.date("[%Y-%m-%d %H:%M:%S] ") .. "Evento registrato\n")
    file:close()
end

L’Iteratore io.lines

io.lines() fornisce un modo elegante per iterare sulle righe di un file. Il file viene chiuso automaticamente alla fine dell’iterazione.

-- Modo diretto con io.lines
for riga in io.lines("configurazione.txt") do
    print(riga)
end

-- Con un handle di file aperto
local file = io.open("dati.txt", "r")
if file then
    for riga in file:lines() do
        print(riga)
    end
    file:close()
end

Gestione degli Errori

Una gestione robusta degli errori e’ fondamentale quando si lavora con i file.

Pattern Base

local function leggi_file_sicuro(percorso)
    local file, errore = io.open(percorso, "r")
    if not file then
        return nil, "Impossibile aprire '" .. percorso .. "': " .. errore
    end

    local contenuto, err_lettura = file:read("*a")
    file:close()

    if not contenuto then
        return nil, "Errore nella lettura: " .. (err_lettura or "sconosciuto")
    end

    return contenuto
end

local testo, err = leggi_file_sicuro("miofile.txt")
if testo then
    print("Contenuto: " .. testo)
else
    print("Errore: " .. err)
end

Uso di pcall per Sicurezza

local ok, risultato = pcall(function()
    local file = assert(io.open("importante.txt", "r"))
    local contenuto = file:read("*a")
    file:close()
    return contenuto
end)

if ok then
    print("Letto con successo: " .. risultato)
else
    print("Errore protetto: " .. risultato)
end

Esempio Pratico: Leggere un File di Configurazione

local function carica_config(percorso)
    local config = {}

    local file = io.open(percorso, "r")
    if not file then
        print("File di configurazione non trovato, uso valori predefiniti")
        return config
    end

    for riga in file:lines() do
        -- Ignora righe vuote e commenti
        if riga ~= "" and not riga:match("^#") then
            local chiave, valore = riga:match("^(%w+)%s*=%s*(.+)$")
            if chiave and valore then
                -- Prova a convertire in numero
                local num = tonumber(valore)
                if num then
                    config[chiave] = num
                elseif valore == "true" then
                    config[chiave] = true
                elseif valore == "false" then
                    config[chiave] = false
                else
                    config[chiave] = valore
                end
            end
        end
    end

    file:close()
    return config
end

-- File config.txt:
-- # Configurazione server
-- host = localhost
-- porta = 8080
-- debug = true
-- nome = MioServer

local cfg = carica_config("config.txt")
for k, v in pairs(cfg) do
    print(k .. " = " .. tostring(v) .. " (" .. type(v) .. ")")
end

Esempio Pratico: Sistema di Logging

local Logger = {}

function Logger.nuovo(percorso_file)
    local self = {percorso = percorso_file}

    function self.scrivi(livello, messaggio)
        local file = io.open(self.percorso, "a")
        if file then
            local timestamp = os.date("%Y-%m-%d %H:%M:%S")
            file:write(string.format("[%s] [%s] %s\n", timestamp, livello, messaggio))
            file:close()
        end
    end

    function self.info(messaggio)
        self.scrivi("INFO", messaggio)
    end

    function self.errore(messaggio)
        self.scrivi("ERRORE", messaggio)
    end

    function self.avviso(messaggio)
        self.scrivi("AVVISO", messaggio)
    end

    function self.leggi_tutto()
        local file = io.open(self.percorso, "r")
        if file then
            local contenuto = file:read("*a")
            file:close()
            return contenuto
        end
        return ""
    end

    return self
end

-- Utilizzo
local log = Logger.nuovo("applicazione.log")
log.info("Applicazione avviata")
log.avviso("Memoria al 80%")
log.errore("Connessione al database fallita")

print(log.leggi_tutto())

Esempio Pratico: Elaborazione CSV

local function leggi_csv(percorso, separatore)
    separatore = separatore or ","
    local dati = {}

    local file = io.open(percorso, "r")
    if not file then
        return nil, "File non trovato"
    end

    local intestazioni = nil
    for riga in file:lines() do
        local campi = {}
        for campo in riga:gmatch("[^" .. separatore .. "]+") do
            table.insert(campi, campo:match("^%s*(.-)%s*$"))  -- trim
        end

        if not intestazioni then
            intestazioni = campi
        else
            local record = {}
            for i, intestazione in ipairs(intestazioni) do
                record[intestazione] = campi[i]
            end
            table.insert(dati, record)
        end
    end

    file:close()
    return dati
end

local function scrivi_csv(percorso, dati, intestazioni, separatore)
    separatore = separatore or ","

    local file = io.open(percorso, "w")
    if not file then
        return false, "Impossibile creare il file"
    end

    -- Scrivi intestazioni
    file:write(table.concat(intestazioni, separatore) .. "\n")

    -- Scrivi dati
    for _, record in ipairs(dati) do
        local valori = {}
        for _, intestazione in ipairs(intestazioni) do
            table.insert(valori, tostring(record[intestazione] or ""))
        end
        file:write(table.concat(valori, separatore) .. "\n")
    end

    file:close()
    return true
end

-- Esempio di utilizzo
local studenti = leggi_csv("studenti.csv")
if studenti then
    for _, s in ipairs(studenti) do
        print(s.nome .. " - " .. s.voto)
    end
end

Conclusione

La gestione dei file in Lua e’ semplice ma efficace. Il modello basato su io.open() e handle di file offre controllo completo sulle operazioni di lettura e scrittura. E’ fondamentale chiudere sempre i file dopo l’uso e gestire correttamente gli errori con controlli su nil. L’iteratore io.lines() semplifica l’elaborazione riga per riga, mentre i diversi formati di file:read() permettono di gestire testo, numeri e dati binari.