Apertura - Lettura

Per aprire e leggere un file possiamo fare in questo modo :

int fd = open("foo.txt", O_RDONLY);
char buffer[512];
ssize_t bytes_read = read(fd, buffer, 512);
close(fd);
printf("Read %zd : %s\n", bytes_read, buffer);
  • apertura del file con open(...) restituisce un file descriptor per le operazioni future
  • la lettura con read(...) restituisce il numero effettivo di byte letti
  • per i flag come O_RDONLY bisogna includere anche #include <fcntl.h> e abbiamo :
    • O_WRONLY : apre in modalità scrittura
    • O_CREAT : crea il file se non esiste
    • O_TRUNC : se il file esiste, tronca il contenuto a dimensione 0
    • si possono usare insieme separandoli da |

Come parametro la call open() prende anche i permessi che verranno dati dal file creato se non esistente, per esempio 0670 :

  • 0 : indica a C che stiamo usando base 8
  • 7 : 4 (read) + 2 (write) + 1 (execute) al owner
  • 4 : 4 (read) al gruppo
  • 0 : nessun permesso a gli altri utenti

Siccome queste system call comunicano con il disco, possono ritornare degli errori (return -1), impostando una variabile globale di sistema errno con un errore specifico tra cui :

  • ENOENT : file does not exist
  • EBADF : bad file descriptor

Posizionamento seek

int fd = open("foo.txt", O_RDONLY);
lseek(fd, 128, SEEK_CUR); // POSIZIONAMENTO SEEK

Attraverso lseek(fd, 128, SEEK_CUR) viene spostata la posizione corrente nel file :

  • sposta il puntatore del file di 128 byte in avanti dalla posizione corrente nel file
  • con SEEK_CUR si indica di considerare la posizione corrente

Scrittura

Per poter scrivere in un file possiamo seguire il seguente esempio :

int fd = open("foo.txt", O_WRONLY | O_CREAT | O_TRUNC);
char buf[] = "Fratmmm!";
write(fd, buf, strlen(buf)); // SCRITTURA FILE
close(fd);

che sovrascriverà il file esistente foot.txt (per O_TRUC) con “Fratmmm!”, oppure creerà il file foo.txt con quel contenuto se non esiste.

Altre operazioni fu file in UNIX

  • unlink("foo.txt") : rimuovere file
  • rename("foo.txt", "bar.txt")
  • chmod("foo.txt", 0755) : cambio permessi file
  • chown("foo.txt", uid, gid) : cambio proprietà file

Scriviamo ora una funzione utility che ci permetta di leggere una riga da un file fino a \n o EOF (end-of-file) :

ssize_t read_line(int fd, char *buffer, ssize_t max_size) {
 
  char c;
  ssize_t read_size = 0;
 
  // leggiamo fino al massimo
  while (read_size < max_size - 1) {
    ssize_t byte_read = read(fd, &c, 1); // leggi 1 solo carattere
    if (byte_read == 0) { // sono arrivato a EOF
      break;
    }
 
    buffer[read_size++] = c;
 
    if (c == '\n') {
      break;
    }
  }
 
  buffer[read_size] = '\0';
  return read_size;
}

Consideriamo ora un esempio che ci permetta di vedere che è possibile scrivere anche roba binaria (diretta nel file come numeri) :

  • Scrittura :
    int file_fd;
    char buffer[BUFFER_SIZE];
 
    // 1. Aprire il file foo.txt e, se non esiste, crearlo.
    file_fd = open(FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (file_fd < 0) {
        perror("Errore nell'aprire il file");
        return 1;
    }
 
    // 2. Scrive un intero in formato binario.
    int my_int_bin = 42; // Intero da scrivere in formato binario
    write(file_fd, &my_int_bin, sizeof(my_int_bin)); // Scrittura binaria
 
    // 3. Scrive un intero in formato leggibile.
    int my_int_text = 43; // Intero da scrivere in formato leggibile
    sprintf(buffer, "%d\n", my_int_text);
    int my_int_string_len = strlen(buffer);
    write(file_fd, buffer, my_int_string_len); // Scrittura testuale
 
    // 4. Scrive un float in formato binario.
    float my_float_bin = 3.14f; // Float da scrivere in formato binario
    write(file_fd, &my_float_bin, sizeof(my_float_bin)); // Scrittura binaria
 
    // 5. Scrive un float in formato leggibile.
    float my_float_text = 2.71f; // Float da scrivere in formato leggibile
    sprintf(buffer, "%.2f\n", my_float_text);
    int my_float_string_len = strlen(buffer);
    write(file_fd, buffer, my_float_string_len); // Scrittura testuale
 
    // 6. Scrive una stringa in formato leggibile.
    const char *my_string_text = "Hello Text"; // Stringa da scrivere
    sprintf(buffer, "%s\n", my_string_text);
    write(file_fd, buffer, strlen(buffer)); // Scrittura testuale
 
    // 7. Scrive una stringa più un numero in formato leggibile.
    const char *message = "My favorite number is"; // Messaggio da scrivere
    int favorite_number = 7; // Numero associato al messaggio
    // Formatta il messaggio e il numero in una stringa leggibile
    sprintf(buffer, "%s %d\n", message, favorite_number);
    // Scrive il contenuto formattato nel file
    write(file_fd, buffer, strlen(buffer));
 
    // 8. Chiude il file dopo la scrittura.
    close(file_fd);
  • Lettura :
    // 9. Riapre il file per la lettura.
    file_fd = open(FILENAME, O_RDONLY);
    if (file_fd < 0) {
        perror("Errore nell'aprire il file per lettura");
        return 1;
    }
 
    // 10. Legge un intero in formato binario.
    int read_int_bin;
    read(file_fd, &read_int_bin, sizeof(read_int_bin)); // Lettura binaria
    printf("Intero letto (binario): %d\n", read_int_bin);
 
    // 11. Legge un intero in formato leggibile. Ma devo ricordare quanti byte ho scritto prima!
    read(file_fd, buffer, my_int_string_len);
    int read_int_text = atoi(buffer); // Conversione da stringa a intero
    printf("Intero letto (testuale): %d\n", read_int_text);
 
    // 12. Legge un float in formato binario.
    float read_float_bin;
    read(file_fd, &read_float_bin, sizeof(read_float_bin)); // Lettura binaria
    printf("Float letto (binario): %.2f\n", read_float_bin);
 
    // 13. Legge un float in formato leggibile. Ancora, devo ricordare quanti byte ho scritto prima!
    read(file_fd, buffer, my_float_string_len);
    float read_float_text = atof(buffer); // Conversione da stringa a float
    printf("Float letto (testuale): %.2f\n", read_float_text);
 
    // 14. Legge il resto del file, una riga alla volta fino alla fine del file (UTILIZZO FUNZIONE DI UTILTY CREATA PRIMA)
    while (read_line(file_fd, buffer, BUFFER_SIZE) > 0) {
        printf("Resto del file. Riga letta: %s", buffer);
    }
 
    // 15. Chiude il file.
    close(file_fd);

Lettura e Scrittura file con libreria standard di C e non POSIX

    /*
     * ---------------------------------------------------------------------
     * 16. Esempio NON POSIX puro: usare fdopen() per scrivere e leggere
     *     in modo semplice tramite stdio (fprintf, fgets, ecc.)
     * ---------------------------------------------------------------------
     *
     * Questo esempio mostra come il C possa essere molto più comodo quando
     * si rinuncia al POSIX "low-level" e si passa all'I/O bufferizzato
     * della libreria standard: FILE*, fprintf(), fgets().
     *
     * Si noti che fdopen() È POSIX, ma fprintf/fgets sono funzioni stdio.
     * Questo approccio NON è più POSIX puro, ma spesso è più pratico.
     */
 
    printf("\n--- Esempio alternativo: scrittura e lettura comoda (stdio) ---\n");
 
    // Usiamo un file diverso per non sovrascrivere l'altro
    const char *FILENAME_ALT = "foo_alt.txt";
 
    // Apertura comoda in scrittura usando fopen() (stdio, NON POSIX puro)
    FILE *fw = fopen(FILENAME_ALT, "w");
    if (!fw) {
        perror("Errore nell'aprire foo_alt.txt per scrittura");
        return 1;
    }
 
    // Scrittura molto più semplice grazie a fprintf()
    fprintf(fw, "Ciao, questa è una riga scritta in modo semplice!\n");
    fprintf(fw, "Scrivo un numero: %d\n", 123);
    fprintf(fw, "Scrivo un float: %.2f\n", 3.14);
    fprintf(fw, "Scrivo anche più righe.\n");
    fprintf(fw, "Tutto senza preoccuparmi di quanti byte scrivo.\n");
 
    fclose(fw); // Chiude il FILE* (stdio)
 
 
    // Ora leggiamo lo stesso file usando fdopen() + fgets()
    int fd_alt = open(FILENAME_ALT, O_RDONLY);
    if (fd_alt < 0) {
        perror("Errore nell'aprire foo_alt.txt per lettura");
        return 1;
    }
 
    // Convertiamo il file descriptor in FILE*
    FILE *fr = fdopen(fd_alt, "r");
    if (!fr) {
        perror("Errore in fdopen() su foo_alt.txt");
        close(fd_alt); // In caso di errore va chiuso a mano
        return 1;
    }
 
    printf("\n--- Lettura da foo_alt.txt tramite fgets() ---\n");
 
    // Lettura semplice con fgets()
    while (fgets(buffer, BUFFER_SIZE, fr)) {
        printf("fgets ha letto: %s", buffer);
    }
 
    // Chiude FILE* (che chiude automaticamente anche il file descriptor)
    fclose(fr);
 
    /*
     * Nota: esiste anche getline(), che può leggere righe di qualunque
     * lunghezza e gestisce da sola la memoria del buffer.
     * Tuttavia getline() NON è POSIX, è GNU/BSD, quindi non lo includiamo.
     */

Esempio copy file


Directory