C Language
Pliki i strumienie we / wy
Szukaj…
Składnia
- #include <stdio.h> / * Dołącz to, aby użyć dowolnej z poniższych sekcji * /
- PLIK * fopen (tryb const char * ścieżka, const char * tryb); / * Otwórz strumień w pliku pod ścieżką w określonym trybie * /
- FILE * freopen (const char * path, const char * mode, FILE * stream); / * Ponownie otwórz istniejący strumień na pliku w ścieżce w określonym trybie * /
- int fclose (strumień FILE *); / * Zamknij otwarty strumień * /
- size_t fread (void * ptr, size_t size, size_t nmemb, FILE * stream); / * Odczytaj ze strumienia co najwyżej nmemb elementy wielkości i zapisuj je w ptr . Zwraca liczbę odczytanych elementów. * /
- size_t fwrite (const void * ptr, size_t size, size_t nmemb, FILE * stream); / * Zapisz nmemb elementy wielkości bajtów każdy z ptr do strumienia . Zwraca liczbę zapisanych elementów. * /
- int fseek (FILE * strumień, długie przesunięcie, int wherece); / * Ustaw kursor strumienia na przesunięcie względem przesunięcia powiedzianego przez wherece i zwraca 0, jeśli się powiedzie. * /
- długi ftell (strumień FILE *); / * Zwraca przesunięcie bieżącej pozycji kursora od początku strumienia. * /
- void przewijanie do tyłu (strumień FILE *); / * Ustaw pozycję kursora na początku pliku. * /
- int fprintf (FILE * fout, const char * fmt, ...); / * Zapisuje ciąg formatu printf na fout * /
- PLIK * stdin; / * Standardowy strumień wejściowy * /
- PLIK * stdout; / * Standardowy strumień wyjściowy * /
- PLIK * stderr; / * Standardowy strumień błędów * /
Parametry
Parametr | Detale |
---|---|
const char * tryb | Ciąg opisujący tryb otwierania strumienia kopii zapasowej pliku. Zobacz uwagi dotyczące możliwych wartości. |
int wherece | Może być SEEK_SET aby ustawić od początku pliku, SEEK_END aby ustawić od końca, lub SEEK_CUR aby ustawić względem bieżącej wartości kursora. Uwaga: SEEK_END nie jest przenośny. |
Uwagi
Ciągi trybów:
Łańcuchy trybów w fopen()
i freopen()
mogą być jedną z następujących wartości:
-
"r"
: Otwórz plik w trybie tylko do odczytu, z kursorem ustawionym na początek pliku. -
"r+"
: Otwórz plik w trybie do odczytu i zapisu, z kursorem ustawionym na początek pliku. -
"w"
: Otwórz lub utwórz plik w trybie tylko do zapisu, a jego zawartość zostanie obcięta do 0 bajtów. Kursor ustawiony jest na początek pliku. -
"w+"
: Otwórz lub utwórz plik w trybie do odczytu i zapisu, a jego zawartość zostanie obcięta do 0 bajtów. Kursor ustawiony jest na początek pliku. -
"a"
: Otwórz lub utwórz plik w trybie tylko do zapisu, z kursorem ustawionym na końcu pliku. -
"a+"
: Otwórz lub utwórz plik w trybie do odczytu i zapisu, z kursorem odczytu ustawionym na początku pliku. Dane wyjściowe zawsze będą jednak dołączane na końcu pliku.
Do każdego z tych trybów plików może być dodany b
po pierwszej literze (np. "rb"
lub "a+b"
lub "ab+"
). b
oznacza, że plik należy traktować jako plik binarny zamiast pliku tekstowego w systemach, w których występuje różnica. Nie robi to różnicy na systemach uniksopodobnych; jest to ważne w systemach Windows. (Dodatkowo system Windows fopen
zezwala na jawne t
zamiast b
na oznaczenie „pliku tekstowego” - i wiele innych opcji specyficznych dla platformy).
-
"wx"
: Utwórz plik tekstowy w trybie tylko do zapisu. Plik może nie istnieć . -
"wbx"
: Utwórz plik binarny w trybie tylko do zapisu. Plik może nie istnieć .
x
, jeśli jest obecny, musi być ostatnim znakiem w ciągu trybu.
Otwórz i zapisz do pliku
#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;
}
Ten program otwiera plik o nazwie podanej w argumencie na main, output.txt
na output.txt
jeśli nie podano argumentu. Jeśli plik o tej samej nazwie już istnieje, jego zawartość jest odrzucana, a plik jest traktowany jako nowy pusty plik. Jeśli pliki jeszcze nie istnieją, wywołuje je wywołanie fopen()
.
Jeśli z jakiegoś powodu wywołanie fopen()
nie powiedzie się, zwraca wartość NULL
i ustawia wartość globalnej zmiennej errno
. Oznacza to, że program może przetestować zwróconą wartość po wywołaniu fopen()
i użyć funkcji perror()
jeśli fopen()
nie powiedzie.
Jeśli wywołanie fopen()
powiedzie się, zwraca poprawny wskaźnik FILE
. Ten wskaźnik może być następnie użyty do odwołania się do tego pliku, dopóki nie zostanie do niego wywołana fclose()
.
Funkcja fputs()
zapisuje podany tekst do otwartego pliku, zastępując wcześniejszą zawartość pliku. Podobnie jak fopen()
, funkcja fputs()
również ustawia wartość errno
jeśli się nie powiedzie, chociaż w tym przypadku funkcja zwraca EOF
wskazując błąd (w przeciwnym razie zwraca wartość nieujemną).
Funkcja fclose()
opróżnia wszystkie bufory, zamyka plik i zwalnia pamięć wskazywaną przez FILE *
. fputs()
wartość wskazuje zakończenie tak samo, jak fputs()
to fputs()
(choć zwraca „0”, jeśli się powiedzie), ponownie również ustawiając wartość errno
w przypadku niepowodzenia.
fprintf
Możesz użyć fprintf
na pliku tak samo, jak na konsoli z printf
. Na przykład, aby śledzić wygrane, straty i remisy w grze, które możesz napisać
/* 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);
}
Uwaga dodatkowa: niektóre systemy (niesławnie Windows) nie używają tego, co większość programistów nazwałaby „normalnymi” zakończeniami linii. Podczas gdy systemy podobne do UNIX używają \ n do zakończenia linii, Windows używa pary znaków: \ r (powrót karetki) i \ n (znak wiersza). Ta sekwencja jest powszechnie nazywana CRLF. Jednak za każdym razem, gdy używasz C, nie musisz się martwić o te szczegóły zależne od platformy. Kompilator AC jest wymagany do przekonwertowania każdego wystąpienia \ n na poprawne zakończenie linii platformy. Kompilator Windows przekonwertuje \ n na \ r \ n, ale kompilator UNIX zachowa go takim, jaki jest.
Uruchom proces
#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;
}
Ten program uruchamia proces ( netstat
) za pomocą popen()
i odczytuje wszystkie standardowe dane wyjściowe z procesu i powtarza to na standardowe wyjście.
Uwaga: popen()
nie istnieje w standardowej bibliotece C , ale jest raczej częścią POSIX C )
Pobierz linie z pliku za pomocą getline ()
Biblioteka POSIX C definiuje funkcję getline()
. Ta funkcja przydziela bufor do przechowywania zawartości wiersza i zwraca nowy wiersz, liczbę znaków w wierszu i rozmiar bufora.
Przykładowy program, który pobiera każdą linię z 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;
}
Plik wejściowy 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.
Wynik
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:
W tym przykładzie funkcja getline()
jest początkowo wywoływana bez przydzielonego bufora. Podczas pierwszego wywołania getline()
przydziela bufor, odczytuje pierwszy wiersz i umieszcza jego zawartość w nowym buforze. Przy kolejnych wywołaniach getline()
aktualizuje ten sam bufor i przenosi go ponownie tylko wtedy, gdy nie jest już wystarczająco duży, aby zmieścił się w całej linii. Tymczasowy bufor jest następnie zwalniany, gdy skończymy z plikiem.
Inną opcją jest getdelim()
. Jest to to samo, co getline()
z tą różnicą, że określasz znak końca linii. Jest to konieczne tylko wtedy, gdy ostatnim znakiem linii dla Twojego typu pliku nie jest „\ n”. getline()
działa nawet z plikami tekstowymi Windows, ponieważ z wielobajtowym zakończeniem linii ( "\r\n")
„\ "\r\n")
jest wciąż ostatnim znakiem w linii.
Przykładowa implementacja 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
Otwórz i zapisz do pliku binarnego
#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;
}
Ten program tworzy i zapisuje tekst w formie binarnej poprzez funkcję fwrite
do pliku output.bin
.
Jeśli plik o tej samej nazwie już istnieje, jego zawartość jest odrzucana, a plik jest traktowany jako nowy pusty plik.
Strumień binarny to uporządkowana sekwencja znaków, która może w przejrzysty sposób rejestrować dane wewnętrzne. W tym trybie bajty są zapisywane między programem a plikiem bez żadnej interpretacji.
Aby zapisywać liczby całkowite w sposób przenośny, należy wiedzieć, czy format pliku oczekuje ich w formacie dużym, czy małym endianem, oraz rozmiar (zwykle 16, 32 lub 64 bity). Przesunięcie i maskowanie bitów może być następnie użyte do zapisania bajtów we właściwej kolejności. Nie ma gwarancji, że liczby całkowite w C mają reprezentację dopełnienia do dwóch (chociaż prawie wszystkie implementacje mają). Na szczęście konwersja do unsigned gwarantuje użyć kod uzupełnień do dwóch. Kod do zapisywania liczb całkowitych ze znakiem w pliku binarnym jest więc nieco zaskakujący.
/* 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;
}
Pozostałe funkcje są zgodne z tym samym wzorem z niewielkimi modyfikacjami rozmiaru i kolejności bajtów.
fscanf ()
Załóżmy, że mamy plik tekstowy i chcemy przeczytać wszystkie słowa w tym pliku, aby spełnić pewne wymagania.
plik.txt :
This is just
a test file
to be used by fscanf()
To jest główna funkcja:
#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++;
}
}
Dane wyjściowe będą:
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()
Czytaj wiersze z pliku
Nagłówek stdio.h
definiuje funkcję fgets()
. Ta funkcja odczytuje wiersz ze strumienia i zapisuje go w określonym ciągu. Funkcja przestaje czytać tekst ze strumienia po odczytaniu n - 1
znaków, odczytaniu znaku nowej linii ( '\n'
) lub osiągnięciu końca pliku (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);
}
}
Wywołanie programu z argumentem będącym ścieżką do pliku zawierającego następujący tekst:
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.
Spowoduje następujące wyniki:
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]:
Ten bardzo prosty przykład pozwala na ustalenie maksymalnej długości linii, tak że dłuższe linie będą skutecznie liczone jako dwie linie. Funkcja fgets()
wymaga, aby kod wywołujący zapewniał pamięć, która zostanie wykorzystana jako miejsce docelowe czytanej linii.
POSIX udostępnia funkcję getline()
która zamiast tego wewnętrznie przydziela pamięć, aby powiększyć bufor w razie potrzeby dla linii o dowolnej długości (o ile jest wystarczającej ilości pamięci).