Articolo originale: https://www.freecodecamp.org/news/file-handling-in-c-how-to-open-close-and-write-to-files/

Se in precedenza hai già scritto il programma C helloworld, conosci già le basi dell'I/O dei file in C:

/* Un semplice hello world in C. */
#include <stdlib.h>

// Importa funzioni I/O.
#include <stdio.h>

int main() {
    // Questo printf è dove avviene tutta la magia IO del file!
    // Che emozione!
    printf("Hello, world!\n");
    return EXIT_SUCCESS;
}

La gestione dei file è una delle parti più importanti della programmazione. In C, utilizziamo un puntatore a una struttura di tipo file per dichiarare un file:

FILE *fp;

C fornisce una serie di funzioni integrate per eseguire operazioni di base sui file:

  • fopen()- crea un nuovo file o apri un file esistente
  • fclose()- chiude un file
  • getc()- legge un carattere da un file
  • putc()- scrive un carattere in un file
  • fscanf()- legge un insieme di dati da un file
  • fprintf()- scrive un insieme di dati in un file
  • getw()- legge un numero intero da un file
  • putw()- scrive un numero intero in un file
  • fseek()- imposta la posizione sul punto desiderato
  • ftell()- fornisce la posizione corrente nel file
  • rewind()- imposta la posizione al punto iniziale

Apertura di un file

La funzione fopen() viene utilizzata per creare un file o aprire un file esistente:

fp = fopen(const char nomefile,const char modalità);

Ci sono molte modalità per aprire un file:

  • r - aprire un file in modalità lettura
  • w - apre o crea un file di testo in modalità scrittura
  • a - apre un file in modalità "aggiungi"
  • r+ - apre un file sia in lettura che in scrittura
  • a+ - apre un file sia in lettura che in scrittura
  • w+ - apre un file sia in lettura che in scrittura

Ecco un esempio di lettura di dati da un file e di scrittura su di esso:

#include<stdio.h>
#include<conio.h>
main()
{
FILE *fp;
char ch;
fp = fopen("hello.txt", "w");
printf("Inserisci il dato: ");
while( (ch = getchar()) != EOF) {
  putc(ch,fp);
}
fclose(fp);
fp = fopen("hello.txt", "r");

while( (ch = getc(fp)! = EOF)
  printf("%c",ch);
  
fclose(fp);
}

Ora potresti pensare: "Questo stampa solo il testo sullo schermo. Come fa a essere IO su file?"

La risposta non è così ovvia all'inizio e richiede una certa comprensione del sistema UNIX. In un sistema UNIX, tutto viene trattato come un file, il che vuol dire che puoi leggere e scrivere su di esso.

Quindi anche il tuo printer può essere astratto ad un file poiché quello che fai con un printer è scrivere con esso. È anche utile pensare a questi file come flussi, poiché come vedrai in seguito, puoi reindirizzarli con la shell.

Quindi, come si collega questo a helloworld e all'IO sui file?

Quando si chiama printf, in realtà si sta semplicemente scrivendo su un file speciale chiamato stdout, abbreviazione di standard output . stdout rappresenta l'output standard come deciso dalla tua shell, che di solito è il terminale. Questo spiega perché è stato stampato sullo schermo.

Ci sono altri due flussi (es. file) che sono disponibili per te con un certo impegno, stdin e stderr. stdin rappresenta lo standard input, che la tua shell di solito collega alla tastiera. stderr rappresenta l'output di errore standard , che la tua shell di solito lega al terminale.

IO rudimentale su File

Basta teoria, mettiamoci al lavoro scrivendo del codice! Il modo più semplice per scrivere su un file è reindirizzare il flusso di output utilizzando lo strumento di reindirizzamento dell'output, >.

Se vuoi effettuare una aggiunta alla fine del file, puoi usare >>:

# Questo stamperà sullo schermo...
./helloworld
# ...ma questo scriverà nel file!
./helloworld > hello.txt

Il contenuto di hello.txt, non a caso, sarà

Hello, world!

Supponiamo di avere un altro programma chiamato greet, simile a helloworld, che ti saluta con un dato name:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // Inizializza un array per contenre il nome.
    char nome[20];
    // Leggi la stringa e salvala in nome.
    scanf("%s", nome);
    // Print the greeting.
    printf("Ciao, %s!", nome);
    return EXIT_SUCCESS;
}

Invece di leggere dalla tastiera, possiamo reindirizzare stdin alla lettura da un file utilizzando lo strumento <:

# Scrivi un file contenente un nome.
echo Kamala > name.txt
# Questo leggerà il nome dal file e stamperà il saluto sullo schermo.
./greet < name.txt
# ==> Ciao, Kamala!
# Se vuoi anche scrivere il saluto su un file, puoi farlo usando ">".

Nota: questi operatori di reindirizzamento sono simili in bash e shell.

Il vero affare

I metodi di cui sopra funzionano solo per i casi più elementari. Se vuoi fare cose più complicate e fatte meglio, probabilmente dovrai lavorare con i file all'interno di C invece che attraverso la shell.

Per fare ciò, utilizzerai una funzione chiamata fopen. Questa funzione accetta due parametri stringa, il primo è il nome del file e il secondo è la modalità.

Le modalità sono fondamentalmente delle autorizzazioni, quindi r per leggere, w scrivere, a aggiungere. Puoi anche combinarli, quindi rw significa che potresti leggere e scrivere sul file. Ci sono più modalità, ma queste sono quelle più comunemente usate.

Dopo aver ottenuto un puntatore FILE, puoi utilizzare praticamente gli stessi comandi IO che hai già usato, tranne per il fatto che devi anteporre loro una f e il primo argomento sarà il puntatore del file. Ad esempio, la versione per i file di printf è fprintf.

Ecco un programma chiamato greetings che legge dati da un file contenente un elenco di nomi e scrive i saluti in un altro file:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // Crea i puntatori ai file.
    FILE *names = fopen("names.txt", "r");
    FILE *greet = fopen("greet.txt", "w");

    // Controlla che tutto sia OK.
    if (!names || !greet) {
        fprintf(stderr, "Apertura del file fallita!\n");
        return EXIT_FAILURE;
    }

    // Tempo di saluti!
    char nome[20];
    // Fondamentalmente continua a leggere finché non rimane più niente.
    while (fscanf(names, "%s\n", nome) > 0) {
        fprintf(greet, "Ciao, %s!\n", nome);
    }

    // Una volta raggiunta la fine, stampa un messaggio sul terminale per informare l'utente.
    if (feof(names)) {
        printf("I saluti sono terminati!\n");
    }

    return EXIT_SUCCESS;
}

Supponiamo che names.txt contenga quanto segue:

Kamala
Logan
Carol

Quindi dopo aver eseguito greetings il file greet.txt conterrà:

Ciao, Kamala!
Ciao, Logan!
Ciao, Carol!