Lettura e Scrittura di File in Windows
In questa lezione vedremo come leggere e scrivere un file in Windows attraverso il linguaggio C con le funzioni ReadFile
e WriteFile
.
Vedremo anche un esempio di programma che legge il contenuto di un file e lo copia in un altro file.
Lettura di un File
Nella lezione precedente abbiamo visto come creare o aprire un file in Windows attraverso la funzione CreateFile
. Adesso vediamo come leggere il contenuto di un file attraverso la funzione ReadFile
.
La funzione ReadFile
è definita nel file Windows.h
e ha la seguente firma:
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
Vediamo nel dettaglio i parametri della funzione ReadFile
:
-
hFile
: è l'handle del file da leggere. Questo parametro deve essere un valore restituito dalla funzioneCreateFile
. -
lpBuffer
: è un puntatore ad un buffer che conterrà i dati letti dal file. Questo parametro deve essere un puntatore ad un buffer di dimensione almeno pari anNumberOfBytesToRead
. -
nNumberOfBytesToRead
: è il numero di byte da leggere dal file. Questo parametro deve essere un valore maggiore di 0. -
lpNumberOfBytesRead
: è un puntatore ad una variabile di tipoDWORD
che conterrà il numero di byte letti dal file. -
lpOverlapped
: è un puntatore ad un oggetto di tipoOVERLAPPED
che contiene informazioni sulle operazioni di I/O asincrone. Per il momento possiamo lasciare questo parametro aNULL
. Quando vedremo le operazioni di I/O asincrone torneremo su questo parametro.
La funzione ReadFile
restituisce TRUE
se la lettura è andata a buon fine, FALSE
altrimenti.
Per poter utilizzare, quindi, la funzione ReadFile
dobbiamo prima creare o aprire un file attraverso la funzione CreateFile
. Successivamente dobbiamo allocare un buffer di dimensione pari al numero di byte che vogliamo leggere dal file. Infine, dobbiamo chiamare la funzione ReadFile
passando come parametri l'handle del file, il buffer, il numero di byte da leggere e un puntatore ad una variabile di tipo DWORD
che conterrà il numero di byte letti dal file.
Chiariamo il tutto con un esempio.
Esempio di Lettura di un File
Proviamo a realizzare un programma che legge un file, il cui percorso è passato come argomento da linea di comando, e stampa il suo contenuto sullo standard output. Inoltre, il programma stampa anche il numero di byte letti dal file al termine della lettura.
Il programma che andremo a realizzare è il seguente:
/*
* FileReader.c
* ------------
* Legge il contenuto di un file e lo stampa sullo standard output.
* Stampa anche il numero di byte letti dal file.
*/
#include <windows.h>
#include <stdio.h>
// Dimensione del buffer di lettura
#define BUFFER_SIZE 1024
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Handle del file da leggere
HANDLE hFile;
// Numero totale di byte letti dal file
DWORD dwTotalNumberOfBytesRead = 0;
// Numero di byte letti dal file
DWORD dwNumberOfBytesRead;
// Buffer che conterrà il contenuto del file
char lpBuffer[BUFFER_SIZE + 1];
// Apertura del file
hFile = CreateFile(
lpCmdLine,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
// Controlliamo se l'apertura del file è andata a buon fine
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Impossibile aprire il file", "Errore", MB_OK | MB_ICONERROR);
return 1;
}
// Ciclo di lettura del file
while (
ReadFile(hFile, lpBuffer, BUFFER_SIZE, &dwNumberOfBytesRead, NULL) &&
dwNumberOfBytesRead > 0)
{
// Inserimento del carattere di fine stringa
lpBuffer[dwNumberOfBytesRead] = '\0';
// Stampa del contenuto del file
printf("%s", lpBuffer);
// Aggiornamento del numero totale di byte letti
dwTotalNumberOfBytesRead += dwNumberOfBytesRead;
}
// Stampa del numero di byte letti dal file
printf("\n\n*****************************************\n");
printf("Numero di byte letti: %d\n", dwTotalNumberOfBytesRead);
CloseHandle(hFile);
return 0;
}
Se proviamo a compilare il file sorgente e ad eseguirlo passando come argomento il percorso di un file, il programma stamperà il contenuto del file sullo standard output e il numero di byte letti dal file. Ad esempio, se il file test.txt
contiene il testo Hello World!
, il programma stamperà il seguente output:
C:\>programma.exe test.txt
Hello World!
*****************************************
Numero di byte letti: 12
Facciamo qualche osservazione sull'esempio:
-
Il buffer di lettura ha dimensione
BUFFER_SIZE + 1
. Questo perché, dopo aver letto i byte dal file, dobbiamo inserire il carattere di fine stringa\0
per poter stampare il contenuto del file sullo standard output. La funzioneReadFile
non inserisce il carattere di fine stringa\0
alla fine del buffer. -
La condizione del ciclo
while
è la seguente:ReadFile(hFile, lpBuffer, BUFFER_SIZE, &dwNumberOfBytesRead, NULL) && dwNumberOfBytesRead > 0
Questa condizione è necessaria per due motivi:
- La funzione
ReadFile
restituisceTRUE
se la lettura è andata a buon fine,FALSE
altrimenti. - Quando la funzione
ReadFile
restituisceTRUE
, il parametrolpNumberOfBytesRead
contiene il numero di byte letti dal file. Se il valore dilpNumberOfBytesRead
è 0, significa che il file è stato letto completamente. InfattiReadFile
non restituisceFALSE
quando raggiunge la fine del file.
- La funzione
Scrittura di un File
La prossima funzione che vedremo è la funzione WriteFile
. Questa funzione permette di scrivere dei dati in un file.
La funzione WriteFile
è definita nel file Windows.h
e ha la seguente firma:
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
Vediamo nel dettaglio i parametri della funzione WriteFile
:
-
hFile
: è l'handle del file in cui scrivere. Questo parametro deve essere un valore restituito dalla funzioneCreateFile
. -
lpBuffer
: è un puntatore ad un buffer che contiene i dati da scrivere nel file. Questo parametro deve essere un puntatore ad un buffer di dimensione almeno pari anNumberOfBytesToWrite
. -
nNumberOfBytesToWrite
: è il numero di byte da scrivere nel file. Questo parametro deve essere un valore maggiore di 0. -
lpNumberOfBytesWritten
: è un puntatore ad una variabile di tipoDWORD
che conterrà il numero di byte scritti nel file. -
lpOverlapped
: è un puntatore ad un oggetto di tipoOVERLAPPED
che contiene informazioni sulle operazioni di I/O asincrone. Per il momento possiamo lasciare questo parametro aNULL
. Quando vedremo le operazioni di I/O asincrone torneremo su questo parametro.
La funzione WriteFile
restituisce TRUE
se la scrittura è andata a buon fine, FALSE
altrimenti.
Anche in questo caso, per poter utilizzare la funzione WriteFile
dobbiamo prima creare o aprire un file attraverso la funzione CreateFile
. Successivamente dobbiamo allocare un buffer di dimensione pari al numero di byte che vogliamo scrivere nel file. Infine, dobbiamo chiamare la funzione WriteFile
passando come parametri l'handle del file, il buffer, il numero di byte da scrivere e un puntatore ad una variabile di tipo DWORD
che conterrà il numero di byte scritti nel file.
Chiariamo il tutto con un esempio.
Esempio di Scrittura di un File
Proviamo a realizzare un programma che crea un nuovo file il cui nome è passato come argomento da linea di comando e scrive i primi 10 numeri interi e il loro quadrato nel file. Inoltre, il programma stampa anche il numero di byte scritti nel file al termine della scrittura.
Il programma che andremo a realizzare è il seguente:
/*
* Squares.c
* ---------
* Crea un file e scrive i primi 10 numeri interi e il loro quadrato nel file.
* Stampa anche il numero di byte scritti nel file.
*/
#include <windows.h>
#include <stdio.h>
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Handle del file da scrivere
HANDLE hFile;
// Numero totale di byte scritti nel file
DWORD dwTotalNumberOfBytesWritten = 0;
// Numero di byte scritti nel file
DWORD dwNumberOfBytesWritten;
// Buffer che conterrà i dati da scrivere nel file
char lpBuffer[100];
// Apertura del file
hFile = CreateFile(
lpCmdLine,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
// Controlliamo se l'apertura del file è andata a buon fine
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Impossibile aprire il file", "Errore", MB_OK | MB_ICONERROR);
return 1;
}
// Ciclo di scrittura nel file
for (int i = 0; i < 10; i++)
{
// Scrittura del numero intero e del suo quadrato nel buffer
sprintf(lpBuffer, "%d %d\n", i, i * i);
// Scrittura del buffer nel file
WriteFile(hFile, lpBuffer, strlen(lpBuffer), &dwNumberOfBytesWritten, NULL);
// Aggiornamento del numero totale di byte scritti
dwTotalNumberOfBytesWritten += dwNumberOfBytesWritten;
}
// Stampa del numero di byte scritti nel file
MessageBox(NULL, "Scrittura completata", "Informazione", MB_OK | MB_ICONINFORMATION);
printf("Numero di byte scritti: %d\n", dwTotalNumberOfBytesWritten);
CloseHandle(hFile);
return 0;
}
Se proviamo a compilare il file sorgente e ad eseguirlo passando come argomento il nome di un file, il programma scriverà i primi 10 numeri interi e il loro quadrato nel file e stamperà il numero di byte scritti nel file. Ad esempio, se lanciamo il programma in questo modo:
C:\>programma.exe quadrati.txt
Numero di byte scritti: 46
Il file quadrati.txt
conterrà il seguente testo:
0 0
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
Esempio completo: Copia di un File in un Altro File
Abbiamo visto le due funzioni principali per leggere e scrivere un file in Windows. Adesso vediamo un esempio completo che legge il contenuto di un file e lo copia in un altro file.
Il programma che andremo a realizzare prende il nome di due file come argomenti da linea di comando. Il programma legge il contenuto del primo file e lo copia nel secondo file. Inoltre, il programma stampa anche il numero di byte letti e scritti.
Il programma che andremo a realizzare è il seguente:
/*
* FileCopy.c
* ----------
* Legge il contenuto di un file e lo copia in un altro file.
* Stampa anche il numero di byte letti e scritti.
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
// Dimensione del buffer di lettura
#define BUFFER_SIZE 1024
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Handle del file da leggere
HANDLE hFileIn;
// Handle del file in cui scrivere
HANDLE hFileOut;
// Numero totale di byte letti dal file
DWORD dwTotalNumberOfBytesRead = 0;
// Numero totale di byte scritti nel file
DWORD dwTotalNumberOfBytesWritten = 0;
// Numero di byte letti dal file
DWORD dwNumberOfBytesRead;
// Numero di byte scritti nel file
DWORD dwNumberOfBytesWritten;
// Buffer che conterrà il contenuto del file
char lpBuffer[BUFFER_SIZE + 1];
// Nome del file da leggere
LPSTR lpszInputFile;
// Nome del file in cui scrivere
LPSTR lpszOutputFile;
// Ottiene il nome del file da leggere e il nome del file in cui scrivere
if (strlen(lpCmdLine) == 0)
{
MessageBox(NULL, "Nessun file specificato", "Errore", MB_OK | MB_ICONERROR);
return 1;
}
else
{
// Separazione dei due nomi dei file
lpszInputFile = strtok(lpCmdLine, " ");
lpszOutputFile = strtok(NULL, " ");
}
// Apertura del file da leggere
hFileIn = CreateFile(
lpszInputFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
// Controlliamo se l'apertura del file è andata a buon fine
if (hFileIn == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Impossibile aprire il file", "Errore", MB_OK | MB_ICONERROR);
return 1;
}
// Apertura del file in cui scrivere
hFileOut = CreateFile(
lpszOutputFile,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
// Controlliamo se l'apertura del file è andata a buon fine
if (hFileOut == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Impossibile aprire il file", "Errore", MB_OK | MB_ICONERROR);
return 1;
}
// Ciclo di lettura del file
while (
ReadFile(hFileIn, lpBuffer, BUFFER_SIZE, &dwNumberOfBytesRead, NULL) &&
dwNumberOfBytesRead > 0)
{
// Inserimento del carattere di fine stringa
lpBuffer[dwNumberOfBytesRead] = '\0';
// Stampa del contenuto del file
printf("%s", lpBuffer);
// Aggiornamento del numero totale di byte letti
dwTotalNumberOfBytesRead += dwNumberOfBytesRead;
// Scrittura del buffer nel file
WriteFile(hFileOut, lpBuffer, dwNumberOfBytesRead, &dwNumberOfBytesWritten, NULL);
// Aggiornamento del numero totale di byte scritti
dwTotalNumberOfBytesWritten += dwNumberOfBytesWritten;
}
// Stampa del numero di byte letti e scritti
printf("\n\n*****************************************\n");
printf("Numero di byte letti: %d\n", dwTotalNumberOfBytesRead);
printf("Numero di byte scritti: %d\n", dwTotalNumberOfBytesWritten);
CloseHandle(hFileIn);
CloseHandle(hFileOut);
return 0;
}
Se proviamo a compilare il file sorgente e ad eseguirlo passando come argomento il nome di due file, il programma leggerà il contenuto del primo file e lo copierà nel secondo file. Inoltre, il programma stamperà il numero di byte letti e scritti. Ad esempio, se lanciamo il programma in questo modo:
C:\>programma.exe input.txt output.txt
Hello World!
*****************************************
Numero di byte letti: 12
Numero di byte scritti: 12
Il file output.txt
conterrà il seguente testo:
Hello World!
Conclusioni
In questa lezione abbiamo visto come leggere e scrivere un file in Windows attraverso il linguaggio C con le funzioni ReadFile
e WriteFile
.