Ripasso

Settimana scorsa abbiamo parlato di stringhe e caratteri, vi avevo proposto un buon numero di esercizi quindi proviamo a rivedere le soluzioni.

Lettura e scrittura da file

Oggi impariamo a leggere e scrivere dati da e su file di testo. I file sono il mezzo che abbiamo per conservare dell'informazione sul computer in maniera semi-permanente. Le nostre variabili vivono solo in memoria e spariscono una volta fermata l'esecuzione del programma, quindi se é necessario mantenere informazione nel tempo siamo costretti a scriverla da qualche parte.

Come vedremo queste operazioni su file sono molto simili a quello che abbiamo giá imparato per leggere e scrivere da e su terminale.

Aprire un file in lettura

Il pacchetto os mette a disposizione un modo per aprire un file mediante la funzione os.Open(name string) (*File, error), per ora ignorate quel *File e pensate solamente che si tratta del tipo che descrive un file. Se avremo tempo proveremo a parlare di come creare nuovi tipi in Go, ma non é questo il momento. Ogni file che viene aperto deve TASSATIVAMENTE essere chiuso prima della fine del programma, per farlo si usa questa sintassi:

f, _ := os.Open("nome_file.txt") // Apro il file 'nome_file.txt'
// Leggo dati dal file nominato 'nome_file.txt'
f.Close() // Chiudo il file

Creare un file

Il pacchetto os mette a disposizione la funzione os.Create(name string) (*File, error) per creare un nuovo file in modalità lettura/scrittura.

ATTENZIONE Se il file esiste giá i contenuti verranno sovrascritti.

f, _ := os.Create("nuovo.txt") // Creo il file 'nuovo.txt'
// Leggo o scrivo dati da/su 'nuovo.txt'
f.Close() // Chiudo il file

Leggere da file aperto

Una volta aperto un file con os.Open per leggere dati si usa la funzione fmt.Fscan in questo modo:

f, _ := os.Open("nome_file.txt") // Apro il file 'nome_file.txt'

var s string
fmt.Fscan(f, &s) // Leggo una stringa dal file
fmt.Println(s)

f.Close()

Scrivere su file aperto

Una volta aperto un file in lettura con os.Create si possono scrivere una quantità arbitraria di dati:

f, _ := os.Create("nuovo.txt") // Apro il file 'nuovo.txt'

fmt.Fprintln(f, "Questa riga andrà nel file") // Scrivo sul file

f.Close() // Chiudo il file

Aggiungere ad un file esistente

Uno dei problemi della funzione os.Create è che file già esistenti vengono troncati e sovrascritti, per evitare questo si può usare la funzione os.OpenFile(name string, flag int, perm FileMode) (*File, error) che è la generalizzazione delle altre due. Il funzionamento è un pochino più complicato, e permette maggiore controllo. A noi interessa soltanto aggiungere ad un file esistente:

f, _ := os.CreateFile("esistente.txt", O_APPEND, 0666)

fmt.Fprintln("Aggiunto alla fine")

f.Close()

Il pacchetto fmt

Molte delle funzioni del pacchetto fmt hanno degli analoghi che interagiscono con file, generalmente queste funzioni si riconoscono perchè il loro nome é identico a quelle che già conoscete ma con una 'F' come prefisso. Ad esempio fmt.Fprint, fmt.Fscan... tutte queste funzioni accettano come primo argomento un file.

Dei file importanti

L'input e l'output del terminale su sistemi Unix (progenitore di Linux) sono semplicemente dei file, in particolare dei file chiamati standard input e standard output. Le funzioni che agiscono su file possono agire anche su questi due file particolari che sono forniti dal pacchetto os nelle variabili os.Stdin e os.Stdout. os.Stdin è aperto in lettura, mentre os.Stdout è aperto in sola scrittura.

Lo standard error

Un terzo file molto importante, inventato su sistemi Unix è il cosiddetto standard error, questo file viene usato esclusivamente per stampare errori. Il pacchetto os mette ad disposizione questo file, aperto in modalità di scrittura, nella variabile os.Stderr.

Note per la prossima lezione

Al momento le nostre capacità di lettura sono piuttosto limitate, ma più avanti impareremo come gestire meglio l'input: come leggere un file riga per riga, parola per parola ecc. Per ora accontentiamoci

Esercizi

Esercizio 0 - Cat

Problema: Scrivere un programma cat.go che riproduce l'analogo programma UNIX (chiamato cat). Lo scopo di questo programma è stampare il contenuto di file.

Nota: Il funzionamento di cat è il seguente:

cat file1 file2

contenuto di file1

contenuto di file2

siccome noi non abbiamo ancora imparato a trattare gli argomenti dati al programma, lo modificheremo in questo modo:

$ cat

Nomi dei file: **file1 file2**

contenuto di file1

contenuto di file2

Esercizio 1 - Wc

Problema: Scrivere un programma wc.go che riproduce l'analogo programma UNIX (chiamato wc). Lo scopo di questo programma è contare il numero di parole in un file.

Nota: Un problema analogo a quello di cat si ripropone qui. Leggi la nota all'esercizio 0

Esempi di esecuzione:

$ wc

Nome di file: file1 file2

file1: 40 parole

file2: 20 parole

Totale: 60 parole

Esercizio 2 - Database 2

Problema: Scrivere un programma database2.go che amplii il funzionamento del programma analogo visto nella lezione precedente. Dato un file db.txt in cui ogni riga ha il seguente formato:

nome;cognome;email;ultimo voto

stampare le informazioni contenute nel file nel formato discusso alla lezione precedente.

Esercizio 3 - Ricerca di parole

Problema: Scrivere un programma cerca_parole.go che legge in input il nome di un file e una chiave di ricerca e stampi true oppure false se la chiave è presente nel file.

Facoltativo: Provate ad implementare la ricerca in modo da tenere traccia della riga in cui la parola è stata trovata. Il programma dovrà quindi stampare il numero della riga a cui è stata trovata la parola, oppure -1 se la chiave non è presente nel file.

Esempi di esecuzione:

Nome del file: **file1.txt**

Chiave di ricerca: **test**

true

Contenuti di file1.txt:

Questo file è un test per il programma

cerca_parole.go