C Language
File e flussi I / O
Ricerca…
Sintassi
- #include <stdio.h> / * Includi questo per utilizzare una delle seguenti sezioni * /
- FILE * fopen (percorso const char *, modalità const char *); / * Apri un flusso sul file nel percorso con la modalità specificata * /
- FILE * freopen (percorso const char *, modo const char *, flusso FILE *); / * Riapri un flusso esistente sul file nel percorso con la modalità specificata * /
- int fclose (flusso FILE *); / * Chiudi uno stream aperto * /
- size_t fread (void * ptr, size_t size, size_t nmemb, FILE * stream); / * Legge al massimo nmemb di elementi di dimensione ciascuno dallo stream e li scrive in ptr . Restituisce il numero di elementi letti. * /
- size_t fwrite (const void * ptr, size_t size, size_t nmemb, FILE * stream); / * Scrivi nmemb elementi di byte di dimensione ciascuno da ptr allo stream . Restituisce il numero di elementi scritti. * /
- int fseek (flusso FILE *, offset lungo, int quale); / * Imposta il cursore del flusso da sfalsare , relativo all'offset detto da dove , e restituisce 0 se ha avuto successo. * /
- ftell lungo (flusso FILE *); / * Restituisce l'offset della posizione corrente del cursore dall'inizio del flusso. * /
- void rewind (flusso FILE *); / * Imposta la posizione del cursore all'inizio del file. * /
- int fprintf (FILE * fout, const char * fmt, ...); / * Scrive la stringa di formato printf su fout * /
- FILE * stdin; / * Flusso di input standard * /
- FILE * stdout; / * Flusso di output standard * /
- FILE * stderr; / * Flusso di errore standard * /
Parametri
Parametro | Dettagli |
---|---|
const char * mode | Una stringa che descrive la modalità di apertura del flusso supportato da file. Vedi osservazioni per possibili valori. |
int whence | Può essere SEEK_SET per impostare dall'inizio del file, SEEK_END per impostare dalla sua fine, o SEEK_CUR per impostare relativo al valore corrente del cursore. Nota: SEEK_END non è portabile. |
Osservazioni
Stringhe di modalità:
Le stringhe di modalità in fopen()
e freopen()
possono essere uno di quei valori:
-
"r"
: apre il file in modalità di sola lettura, con il cursore impostato all'inizio del file. -
"r+"
: apre il file in modalità lettura-scrittura, con il cursore impostato all'inizio del file. -
"w"
: apre o crea il file in modalità solo scrittura, con il contenuto troncato a 0 byte. Il cursore è impostato all'inizio del file. -
"w+"
: apre o crea il file in modalità lettura-scrittura, con il contenuto troncato a 0 byte. Il cursore è impostato all'inizio del file. -
"a"
: apre o crea il file in modalità solo scrittura, con il cursore impostato alla fine del file. -
"a+"
: apre o crea il file in modalità lettura-scrittura, con il cursore di lettura impostato all'inizio del file. L'output, tuttavia, verrà sempre aggiunto alla fine del file.
Ognuna di queste modalità di file può avere una b
aggiunta dopo la lettera iniziale (es. "rb"
o "a+b"
o "ab+"
). Il b
significa che il file deve essere trattato come un file binario invece di un file di testo su quei sistemi in cui vi è una differenza. Non fa differenza sui sistemi di tipo Unix; è importante sui sistemi Windows. (Inoltre, Windows fopen
consente una t
esplicita invece di b
per indicare 'file di testo' - e numerose altre opzioni specifiche della piattaforma.)
-
"wx"
: crea un file di testo in modalità di sola scrittura. Il file potrebbe non esistere . -
"wbx"
: crea un file binario in modalità solo scrittura. Il file potrebbe non esistere .
La x
, se presente, deve essere l'ultimo carattere nella stringa della modalità.
Apri e scrivi su file
#include <stdio.h> /* for perror(), fopen(), fputs() and fclose() */
#include <stdlib.h> /* for the EXIT_* macros */
int main(int argc, char **argv)
{
int e = EXIT_SUCCESS;
/* Get path from argument to main else default to output.txt */
char *path = (argc > 1) ? argv[1] : "output.txt";
/* Open file for writing and obtain file pointer */
FILE *file = fopen(path, "w");
/* Print error message and exit if fopen() failed */
if (!file)
{
perror(path);
return EXIT_FAILURE;
}
/* Writes text to file. Unlike puts(), fputs() does not add a new-line. */
if (fputs("Output in file.\n", file) == EOF)
{
perror(path);
e = EXIT_FAILURE;
}
/* Close file */
if (fclose(file))
{
perror(path);
return EXIT_FAILURE;
}
return e;
}
Questo programma apre il file con il nome dato nell'argomento principale, per default su output.txt
se non viene fornito alcun argomento. Se esiste già un file con lo stesso nome, il suo contenuto viene scartato e il file viene trattato come un nuovo file vuoto. Se i file non esistono già, la chiamata fopen()
lo crea.
Se la chiamata fopen()
fallisce per qualche motivo, restituisce un valore NULL
e imposta il valore della variabile errno
globale. Ciò significa che il programma può testare il valore restituito dopo la chiamata a fopen()
e usare perror()
se fopen()
fallisce.
Se la chiamata fopen()
positivo, restituisce un puntatore FILE
valido. Questo puntatore può quindi essere utilizzato per fare riferimento a questo file finché non viene chiamato fclose()
.
La funzione fputs()
scrive il testo specificato nel file aperto, sostituendo qualsiasi contenuto precedente del file. Analogamente a fopen()
, la funzione fputs()
imposta anche il valore errno
se fallisce, anche se in questo caso la funzione restituisce EOF
per indicare il fail (altrimenti restituisce un valore non negativo).
La fclose()
svuota tutti i buffer, chiude il file e libera la memoria indicata da FILE *
. Il valore restituito indica il completamento proprio come fa fputs()
(sebbene restituisca '0' se ha successo), anche in questo caso imposta nuovamente il valore errno
nel caso di un errore.
fprintf
È possibile utilizzare fprintf
su un file proprio come si potrebbe su una console con printf
. Ad esempio, per tenere traccia delle vittorie, perdite e legami del gioco che potresti scrivere
/* saves wins, losses and, ties */
void savewlt(FILE *fout, int wins, int losses, int ties)
{
fprintf(fout, "Wins: %d\nTies: %d\nLosses: %d\n", wins, ties, losses);
}
Una nota a margine: alcuni sistemi (famigeratamente, Windows) non usano quello che la maggior parte dei programmatori chiamerebbe "normale" terminazioni di linea. Mentre i sistemi simili a UNIX usano \ n per terminare le linee, Windows usa una coppia di caratteri: \ r (ritorno a capo) e \ n (avanzamento riga). Questa sequenza è comunemente chiamata CRLF. Tuttavia, ogni volta che si utilizza C, non è necessario preoccuparsi di questi dettagli altamente dipendenti dalla piattaforma. Il compilatore AC è necessario per convertire ogni istanza di \ n alla terminazione della linea della piattaforma corretta. Quindi un compilatore di Windows converte \ n in \ r \ n, ma un compilatore UNIX lo manterrebbe così com'è.
Esegui processo
#include <stdio.h>
void print_all(FILE *stream)
{
int c;
while ((c = getc(stream)) != EOF)
putchar(c);
}
int main(void)
{
FILE *stream;
/* call netstat command. netstat is available for Windows and Linux */
if ((stream = popen("netstat", "r")) == NULL)
return 1;
print_all(stream);
pclose(stream);
return 0;
}
Questo programma esegue un processo ( netstat
) tramite popen()
e legge tutto lo standard output dal processo e lo fa eco allo standard output.
Nota: popen()
non esiste nella libreria C standard , ma è piuttosto una parte di POSIX C )
Ottieni linee da un file usando getline ()
La libreria POSIX C definisce la funzione getline()
. Questa funzione alloca un buffer per contenere il contenuto della riga e restituisce la nuova riga, il numero di caratteri nella riga e la dimensione del buffer.
Esempio di programma che ottiene ogni riga da example.txt
:
#include <stdlib.h>
#include <stdio.h>
#define FILENAME "example.txt"
int main(void)
{
/* Open the file for reading */
char *line_buf = NULL;
size_t line_buf_size = 0;
int line_count = 0;
ssize_t line_size;
FILE *fp = fopen(FILENAME, "r");
if (!fp)
{
fprintf(stderr, "Error opening file '%s'\n", FILENAME);
return EXIT_FAILURE;
}
/* Get the first line of the file. */
line_size = getline(&line_buf, &line_buf_size, fp);
/* Loop through until we are done with the file. */
while (line_size >= 0)
{
/* Increment our line count */
line_count++;
/* Show the line details */
printf("line[%06d]: chars=%06zd, buf size=%06zu, contents: %s", line_count,
line_size, line_buf_size, line_buf);
/* Get the next line */
line_size = getline(&line_buf, &line_buf_size, fp);
}
/* Free the allocated line buffer */
free(line_buf);
line_buf = NULL;
/* Close the file now that we are done with it */
fclose(fp);
return EXIT_SUCCESS;
}
Inserisci il file example.txt
This is a file
which has
multiple lines
with various indentation,
blank lines
a really long line to show that getline() will reallocate the line buffer if the length of a line is too long to fit in the buffer it has been given,
and punctuation at the end of the lines.
Produzione
line[000001]: chars=000015, buf size=000016, contents: This is a file
line[000002]: chars=000012, buf size=000016, contents: which has
line[000003]: chars=000015, buf size=000016, contents: multiple lines
line[000004]: chars=000030, buf size=000032, contents: with various indentation,
line[000005]: chars=000012, buf size=000032, contents: blank lines
line[000006]: chars=000001, buf size=000032, contents:
line[000007]: chars=000001, buf size=000032, contents:
line[000008]: chars=000001, buf size=000032, contents:
line[000009]: chars=000150, buf size=000160, contents: a really long line to show that getline() will reallocate the line buffer if the length of a line is too long to fit in the buffer it has been given,
line[000010]: chars=000042, buf size=000160, contents: and punctuation at the end of the lines.
line[000011]: chars=000001, buf size=000160, contents:
Nell'esempio, getline()
viene inizialmente chiamato senza alcun buffer allocato. Durante questa prima chiamata, getline()
assegna un buffer, legge la prima riga e posiziona il contenuto della linea nel nuovo buffer. Nelle chiamate successive, getline()
aggiorna lo stesso buffer e rialloca solo il buffer quando non è più abbastanza grande da adattarsi all'intera linea. Il buffer temporaneo viene quindi liberato quando abbiamo finito con il file.
Un'altra opzione è getdelim()
. È lo stesso di getline()
tranne per il carattere di fine riga. Questo è necessario solo se l'ultimo carattere della linea per il tuo tipo di file non è '\ n'. getline()
funziona anche con i file di testo di Windows perché con la fine della riga multibyte ( "\r\n")
'\ n'` è ancora l'ultimo carattere sulla riga.
Esempio di implementazione di getline()
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#if !(defined _POSIX_C_SOURCE)
typedef long int ssize_t;
#endif
/* Only include our version of getline() if the POSIX version isn't available. */
#if !(defined _POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200809L
#if !(defined SSIZE_MAX)
#define SSIZE_MAX (SIZE_MAX >> 1)
#endif
ssize_t getline(char **pline_buf, size_t *pn, FILE *fin)
{
const size_t INITALLOC = 16;
const size_t ALLOCSTEP = 16;
size_t num_read = 0;
/* First check that none of our input pointers are NULL. */
if ((NULL == pline_buf) || (NULL == pn) || (NULL == fin))
{
errno = EINVAL;
return -1;
}
/* If output buffer is NULL, then allocate a buffer. */
if (NULL == *pline_buf)
{
*pline_buf = malloc(INITALLOC);
if (NULL == *pline_buf)
{
/* Can't allocate memory. */
return -1;
}
else
{
/* Note how big the buffer is at this time. */
*pn = INITALLOC;
}
}
/* Step through the file, pulling characters until either a newline or EOF. */
{
int c;
while (EOF != (c = getc(fin)))
{
/* Note we read a character. */
num_read++;
/* Reallocate the buffer if we need more room */
if (num_read >= *pn)
{
size_t n_realloc = *pn + ALLOCSTEP;
char * tmp = realloc(*pline_buf, n_realloc + 1); /* +1 for the trailing NUL. */
if (NULL != tmp)
{
/* Use the new buffer and note the new buffer size. */
*pline_buf = tmp;
*pn = n_realloc;
}
else
{
/* Exit with error and let the caller free the buffer. */
return -1;
}
/* Test for overflow. */
if (SSIZE_MAX < *pn)
{
errno = ERANGE;
return -1;
}
}
/* Add the character to the buffer. */
(*pline_buf)[num_read - 1] = (char) c;
/* Break from the loop if we hit the ending character. */
if (c == '\n')
{
break;
}
}
/* Note if we hit EOF. */
if (EOF == c)
{
errno = 0;
return -1;
}
}
/* Terminate the string by suffixing NUL. */
(*pline_buf)[num_read] = '\0';
return (ssize_t) num_read;
}
#endif
Apri e scrivi su un file binario
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
result = EXIT_SUCCESS;
char file_name[] = "outbut.bin";
char str[] = "This is a binary file example";
FILE * fp = fopen(file_name, "wb");
if (fp == NULL) /* If an error occurs during the file creation */
{
result = EXIT_FAILURE;
fprintf(stderr, "fopen() failed for '%s'\n", file_name);
}
else
{
size_t element_size = sizeof *str;
size_t elements_to_write = sizeof str;
/* Writes str (_including_ the NUL-terminator) to the binary file. */
size_t elements_written = fwrite(str, element_size, elements_to_write, fp);
if (elements_written != elements_to_write)
{
result = EXIT_FAILURE;
/* This works for >=c99 only, else the z length modifier is unknown. */
fprintf(stderr, "fwrite() failed: wrote only %zu out of %zu elements.\n",
elements_written, elements_to_write);
/* Use this for <c99: *
fprintf(stderr, "fwrite() failed: wrote only %lu out of %lu elements.\n",
(unsigned long) elements_written, (unsigned long) elements_to_write);
*/
}
fclose(fp);
}
return result;
}
Questo programma crea e scrive testo in forma binaria tramite la fwrite
funzione al file output.bin
.
Se esiste già un file con lo stesso nome, il suo contenuto viene scartato e il file viene trattato come un nuovo file vuoto.
Un flusso binario è una sequenza ordinata di caratteri che può registrare in modo trasparente i dati interni. In questa modalità, i byte vengono scritti tra il programma e il file senza alcuna interpretazione.
Per scrivere in modo portabile gli interi, è necessario sapere se il formato del file li prevede in formato big o little-endian e le dimensioni (in genere 16, 32 o 64 bit). Il cambio di bit e il mascheramento possono quindi essere utilizzati per scrivere i byte nell'ordine corretto. Non è garantito che gli interi in C abbiano la rappresentazione del complemento a due (sebbene quasi tutte le implementazioni lo facciano). Fortunatamente una conversione a unsigned è garantita per utilizzare due complementi. Il codice per scrivere un intero con segno su un file binario è quindi un po 'sorprendente.
/* write a 16-bit little endian integer */
int fput16le(int x, FILE *fp)
{
unsigned int rep = x;
int e1, e2;
e1 = fputc(rep & 0xFF, fp);
e2 = fputc((rep >> 8) & 0xFF, fp);
if(e1 == EOF || e2 == EOF)
return EOF;
return 0;
}
Le altre funzioni seguono lo stesso schema con modifiche minori per dimensioni e ordine dei byte.
fscanf ()
Diciamo che abbiamo un file di testo e vogliamo leggere tutte le parole in quel file, al fine di fare alcuni requisiti.
file.txt :
This is just
a test file
to be used by fscanf()
Questa è la funzione principale:
#include <stdlib.h>
#include <stdio.h>
void printAllWords(FILE *);
int main(void)
{
FILE *fp;
if ((fp = fopen("file.txt", "r")) == NULL) {
perror("Error opening file");
exit(EXIT_FAILURE);
}
printAllWords(fp);
fclose(fp);
return EXIT_SUCCESS;
}
void printAllWords(FILE * fp)
{
char tmp[20];
int i = 1;
while (fscanf(fp, "%19s", tmp) != EOF) {
printf("Word %d: %s\n", i, tmp);
i++;
}
}
L'output sarà:
Word 1: This
Word 2: is
Word 3: just
Word 4: a
Word 5: test
Word 6: file
Word 7: to
Word 8: be
Word 9: used
Word 10: by
Word 11: fscanf()
Leggi le righe da un file
L'intestazione stdio.h
definisce la funzione fgets()
. Questa funzione legge una riga da uno stream e la memorizza in una stringa specificata. La funzione interrompe la lettura del testo dallo stream quando vengono letti n - 1
carattere, viene letto il carattere di nuova riga ( '\n'
) o viene raggiunta la fine del file (EOF).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 80
int main(int argc, char **argv)
{
char *path;
char line[MAX_LINE_LENGTH] = {0};
unsigned int line_count = 0;
if (argc < 1)
return EXIT_FAILURE;
path = argv[1];
/* Open file */
FILE *file = fopen(path, "r");
if (!file)
{
perror(path);
return EXIT_FAILURE;
}
/* Get each line until there are none left */
while (fgets(line, MAX_LINE_LENGTH, file))
{
/* Print each line */
printf("line[%06d]: %s", ++line_count, line);
/* Add a trailing newline to lines that don't already have one */
if (line[strlen(line) - 1] != '\n')
printf("\n");
}
/* Close file */
if (fclose(file))
{
return EXIT_FAILURE;
perror(path);
}
}
Chiamando il programma con un argomento che è un percorso di un file contenente il seguente testo:
This is a file
which has
multiple lines
with various indentation,
blank lines
a really long line to show that the line will be counted as two lines if the length of a line is too long to fit in the buffer it has been given,
and punctuation at the end of the lines.
Risulterà nel seguente output:
line[000001]: This is a file
line[000002]: which has
line[000003]: multiple lines
line[000004]: with various indentation,
line[000005]: blank lines
line[000006]:
line[000007]:
line[000008]:
line[000009]: a really long line to show that the line will be counted as two lines if the le
line[000010]: ngth of a line is too long to fit in the buffer it has been given,
line[000011]: and punctuation at the end of the lines.
line[000012]:
Questo esempio molto semplice consente una lunghezza massima della linea fissa, in modo tale che le linee più lunghe vengano effettivamente contate come due linee. La funzione fgets()
richiede che il codice chiamante fornisca la memoria da utilizzare come destinazione per la linea che viene letta.
POSIX rende disponibile la funzione getline()
che invece alloca internamente la memoria per ingrandire il buffer come necessario per una linea di qualsiasi lunghezza (purché vi sia memoria sufficiente).