Перейти к содержимому

Как задать текстовый файл в c

  • автор:

Как задать текстовый файл в c

При работе с текстовыми файлами граздо проще работать с данными не как с отдельными символами, а как со строками с помощью функций fgets() и fputs() .

Запись текстового файла

Функция fputs() записывает в файл строку, то есть набор символов, который завершается символом ‘\0’. Она имеет следующий прототип:

int fputs(const char *s, FILE *stream);

Первый параметр функции — записываемая строка, а второй — указатель на файловый поток. В качестве результата функция возвращает неотрицательное целое число. При ошибках в процессе записи возвращается значение EOF .

При записи строки нулевой символ ‘\0’ в файл не записывается.

Например, запишем строку в файл:

#include int main(void) < // строка для записи char * message = "Hello METANIT.COM!\nAn apple a day keeps the doctor away"; // файл для записи char * filename = "data.txt"; // запись в файл FILE *fp = fopen(filename, "w"); if(fp) < // записываем строку fputs(message, fp); fclose(fp); printf("File has been written\n"); >>

Запись довольно проста: открываем файл на запись и с помощью вызова fputs(message, fp) записываем в файл искомую строку.

Чтение текстового файла

Функция fgets() считывает из файла одну строку и имеет следующий прототип:

char * fgets(char *s, int n, FILE *sream);

Первый параметр char *s представляет строку, в которую производится считывание. Второй параметр — число n указывает, сколько символов необходимо считывать. И третий параметр представляет файловый поток, из которого производится считывание.

При вызове функция считывает из файла не более n-1 символов. Функция прекращает чтение, когда прочтет n-1 символов или встретит символ переноса строки \n. Все считанные символы записываются в строку s, в том числе символ \n. И также конец каждой строки дописывается нулевой символ ‘\0’.

При успешном завершении функция возвращает указатель s , а при ошибке или достижении конца файла возвращается значение NULL .

Считаем данные из выше записанного файла «data.txt»:

#include int main(void) < // файл чтения char * filename = "data.txt"; // буфер для считавания данных из файла char buffer[256]; // чтение из файла FILE *fp = fopen(filename, "r"); if(fp) < // пока не дойдем до конца, считываем по 256 байт while((fgets(buffer, 256, fp))!=NULL) < printf("%s", buffer); >fclose(fp); > >

Здеcь открываем файл на чтение и в цикле считываем из файла по 256 символов и выводим их на консоль с помощью вызова fgets(cc, 256, fp) . Когда данные в файле закончатся, функция возвратит NULL, и произойдет выход из цикла.

Копирование файлов

Другой пример работы с текстовыми файлами — копирование содержимого из одного файла в другой:

#include int main(void) < char * filename1 = "data1.txt"; char * filename2 = "data2.txt"; char buffer[256]; FILE *f1 = fopen(filename1, "r"); // файл на чтение FILE *f2 = fopen(filename2, "w"); // файл на запись if(!f1 || !f2) < printf("Error occured while opening file\n"); >else < // пока не дойдем до конца, считываем по 256 байт из файла f1 while((fgets(buffer, 256, f1))!=NULL) < // записываем строку в файл f2 fputs(buffer, f2); printf("%s", buffer); >> fclose(f1); fclose(f2); return 0; >

Как задать текстовый файл в c

Интуитивное определение файла звучит примерно так. Файл — именованная область на жестком диске. На самом деле с точки зрения ОС UNIX это совсем не так. В ОС UNIX файл — очень удобная абстракция. С точки зрения UNIX файлом называется «что-нибудь», из чего можно считывать информацию или во что можно записывать информацию. Файлы это:

  • Файлы в обычном смысле: файлы, которые хранятся на жестком диске (можно считывать из них и запиcывать в них информацию);
  • Экран монитора: файл, в который можно выводить информацию (отобразится на экране монитора);
  • Клавиатура: файл, из которого можно считывать информацию;
  • Принтер: файл, в который можно выводить информацию (печать текста);
  • Модем: файл, из которого можно считывать информацию и в который можно записывать информацию (обмен информации по сети);

0.2 Разделение понятий файла и названия

Неправильно думать, что между сущностями «файл» и «название файла» есть взаимно однозначное соответствие.

Можно привести аналогию из жизни: если представить, что файл — это банка с некоторым содержимым, то название файла — это этикетка на этой банке. Логично предположить, что у банки может быть несколько этикеток.

С точки зрения UNIX:

Правильно говорить, что у названия есть файл. И наоборот: неправильно говорить, что у файла есть название. Никакого эффективного способа узнать имя файла не существует (но можно перебрать все файлы файловой системы).

0.3 Функции link и unlink

Пусть есть файл file1.txt . Для его удаления используется функция:

int unlink(const char* filename);

Эта функция не всегда удаляет файл (с жесткого диска), а только удаляет «этикетку» этого файла. Если есть другая «этикетка» этого файла, то файл останется на жестком диске; просто у него уже не будет этой «этикетки». Файл знает, сколько у него таких «этикеток» (есть специальный счетчик). И если этот счетчик стал равен нулю, то функция удаляет файл с жесткого диска. В ОС Windows эта функция всегда удаляет файл.

Парная функция к этой функции:

int link(const char* filename1, const char* filename2);

Эта функция создает еще одну «этикетку» для этого файла и прибавляет к значению счетчика «этикеток» единицу.

1 Ввод и вывод, язык C, структура FILE

1.1 Чтение и запись: printf и scanf

Всем хорошо известная функция printf :

printf(«Hello!») — печать текста на экран;
printf(«N = %d», N) — форматированный вывод на экран: вывести число N в десятичной записи;
printf(«N = %x», N) — форматированный вывод на экран: вывести число N в шестнадцатеричной записи;

Аналогично парная функция scanf :

char *ptr = new char[10];
scanf(«%s», ptr); — считывание с клавиатуры строки в массив *ptr

char *ptr = new char[10];
scanf(«%s», ptr);

Тут налицо потенциальная проблема переполнения буфера (в данном примере в буфере всего 10 байт).
Никогда не следует пользоваться scanf ‘ом для чтения строк.

1.2 Чтение и запись файлов: FILE* , fopen , fprintf , fscanf

Есть несколько способов работы с файлами c использованием языков C и C++.

Самый распространенный связан со структурой FILE (это не класс, потому что сущность языка C). Эта структура определена в заголовочном файле стандартной библиотеки . Размер этой структуры и ее поля зависят от ОС и от версии компилятора. Поэтому никто не пользуется структурой FILE . Обычно пользуются указателем на эту структуру: FILE* . Например:

FILE *f = fopen(«file1.txt», «r»);

  1. не существует файла;
  2. у программы недостаточно прав доступа для работы с файлом;

Для дальнейшей корректной работы следует писать примерно такой код:

if (f == NULL) // файл не удалось открыть
>
else // Работа с файлом
>

Допустим, что нам удалось открыть файл, т.е. f != NULL . Тогда для того, чтобы считывать файл, можно использовать функцию:

Эта функция работает аналогично функции scanf . Поэтому использовать эту функцию небезопасно! Все проблемы, перечисленные для scanf ‘а, имеют место и при работе с fscanf ‘ом.

Если мы хотим записать в файл что-то, то мы должны сначала открыть его на запись:

FILE *f = fopen(«file2.html», «w»);
Тут «w» означает, что мы открываем файл на запись (от write). Если файл не существовал, то он создастся и откроется на запись, а если он существовал, то он сначала будет уничтожен, а затем создан заново, и потом файл будет открыт на запись.
Еще один способ открыть файл — это открыть его на дозапись. Это можно сделать с помощью параметра «a» (от append). Если файл не существовал, то он создастся и откроется на запись, а если он существовал, то он откроется на запись, и запись будет производится в конец файла.

Затем можно использовать функцию fprintf(f, . )

1.2.1 Зачем нужно закрывать файлы
  • Зададимся вопросом: «Что надо сделать после того, как мы поработали с файлом?»
    Формальный ответ: «Закрыть файл.» Это можно сделать с помощью функции:
    fclose(f);

Но зачем это делать?

Ввиду механического устройства жесткого диска, данные в файл попадают не сразу. Сначала данные записываются в так называемый буфер (область оперативной памяти), и когда он переполнится, то данные из буфера будут записаны в файл. Такая схема придумана для ускорения работы с файлами. На самом деле, буфер — это поле структуры FILE : указатель на массив char ‘ов.

1.2.2 Важность буфера при работе с файлами
  • Рассмотрим следующую ситуацию. Программа пишет протокол своих действий в файл (например, с помощью функции fprintf ). Допустим, что программа сломалась. Понятно, что скорее всего получится так, что в файл последний fprintf (последний протокол действий) не запишется. Причина тому — это буфер.
    Чтобы «протолкнуть» буфер в файл, используется функция

В коде это выглядит примерно так:

1.3 Стандартные уже открытые файлы: stdin, stdout, stderr

С точки зрения UNIX клавиатура и экран — это файлы.

Есть три стандартные константы:
FILE *stdin
FILE *stdout
FILE *stderr

Это три стандартных заранее открытых файла.

stdin — это стандартный файл (поток) ввода, а stdout — стандартный файл (поток) вывода. Таким образом:
scanf(. ) в точности эквивалентно fscanf(stdin, . )
printf(. ) в точности эквивалентно fprintf(stdout, . )

Такой гибкостью можно воспользоваться при написании программы для работы с файлами. Например, для отладки программы можно выводить информацию на экран монитора, а не в файл. Для этого в начале работы с файлом пишем две строчки:

//FILE *f = fopen(. );
FILE *f = stdin;

При этом код программы будет содержать такие функции: fscanf(f, . ) или fprintf(f, . ) . А когда отладка законичится, просто снимаем/ставим соответствующие комментарии в двух строчках программы.

stderr — это стандартный файл (поток) ошибок. По умолчанию выводит данные на экран.
Но существует заметное отличие этого «файла» от stdin и stdout : stderr — небуферизованный файл (поток). Поэтому в этот файл (поток) все байты уходят без «задержки», которая могла бы возникнуть при буферизированном подходе. Понятно, что польза от этого подхода заключается в том, что вместо кода:

1.4 Текстовые и бинарные файлы; что меняет опция t/b

fopen(f, «file1.txt», «w»);

Почему второй параметр «w» является строкой, а не символом?
На самом деле бывает много способов прочитать/записать файл. Например:
fopen(«file1.txt», «wt») — откроет файл как текстовый файл;
fopen(«file1.txt», «wb») — откроет файл как бинарный файл.

Но в чем отличие?

Разница заключается лишь в том, что символы переноса строк запишутся по разному.
Рассмотрим пример в UNIX и Windows:

Исходная строка кода выглядит так: fprintf(«Hello\n»);

    Откроем в Windows файл на запись с параметром «wb» (как бинарный файл). Это означает, что в него запишется в точности то, что мы передали в функции fprintf . Тогда в файл запишутся ровно 6 байт: Hello\10

1.5 Как же читать/писать на самом деле: fgets , fread и fwrite

Использование функций ptintf и scanf для записи и для чтения — это очень плохая идея. Тогда все-таки как лучше читать и записывать?

Хороший способ чтения из файла дает функция fgets() (от «get string»):

char *fgets(char *buffer, size_t length, FILE *file);

  • buffer — это указатель на буфер, в который мы читаем;
  • length — это размер буфера;
  • file — это файл, из которого мы читаем (если читаем с клавиатуры, то разумно использовать stdin ).
  • Функция возвращает строку

Налицо быстрота и безопасность. Главное отличие от scanf ‘а заключается в том, что функция перестанет читать в тот момент, когда закончится буфер. Быстрота обусловлена тем, что функция scanf должна в момент выполнения разобрать форматную строку, в то время как fgets просто читает строку.

1.5.1 Как доставать числа? Семейство atoi, sscanf

В то время как фунция fgets читеат обычную строку, функция scanf может читать и различные другие типы (целые, вещественные числа).

В языке C есть семейство функций ~ atoi (a — ASCII , i — integer ):

Функция принимает единственный параметр строку и пытается ее привести в типу int . Надо заметить, что функция atoi безопасная, но не очень удобная. Безопасная в том смысле, что не сломается: atoi(«25a») == 25 . «Неудобства» заключаются в том, то если мы передаем в качестве параметра строку, в которой есть не только числа, нужно быть очень внимательным и знать, как работает эта функция. Функция atoi никак не проинформирует нас, если преобразование прошло неудачно.

Например, atoi(«abc») == 0 , что на самом деле не совсем соостветствует действительности. Использовать функцию atoi нужно лишь в том случае, когда вы уверены, что в строке есть число.

Родственные функции: atol, atoll, atof, strtol .

Им соответствуют функции для преобразования в типы long , long long и float .

Рассмотрим подробнее strtol :

long strtol(char *buffer, char **endPtr, int base);

  • buffer — это указатель на буфер, из которого мы читаем;
  • endPtr — это переменная, которая используется для того, чтобы сообщить нам насколько успешно произошло преобразование;
    это указатель на char * , в котором записан первый символ, который не смог проинтерпретироваться с помощью функции strtol ;
    Применение выглядит примерно так:

char *end;
char *ptr = «25a»;
int N = strtol(ptr, &end, 10);

Теперь end указывает на «a».

if (ptr == end) <
// ничего не получилось прочитать
>

Более мощное средство

Есть более мощное средство, чем нежели fgets + atoi . Речь идет о функции sscanf .
Вместо использования функции fscanf(f, «%d», &N) можно использовать связку:

fgets(ptr, 100, f);
sscanf(ptr, «%d», &N);

В чем преимущество и мощность такого подхода?

  • Мы знаем длину того, что мы прочитали;
  • Рассмотрим следующую ситуацию: мы хотим прочитать какие-то данные, но не смогли из-за ошибки.

Используя такой подход, мы можем передать пользователю сообщение об ошибке и ту строчку, которую нам не удалось прочитать: ptr . С использованием fscanf ‘а это невозможно.

1.5.2 fread и fwrite

На самом деле не все файлы выглядят как текст. Файле могут быть записаны числовые данные.

size_t fread(void *ptr, size_t size, size_t nelts, FILE *f);

  • void *ptr — указатель на ту область памяти, в которую мы читаем;
  • size_t size — размер элемента, который мы читаем;
  • size_t nelts — максимальное количество элементов, которые можно записать;
  • FILE *f — файл, из которого читаем;
  • size_t fread() — сама функция возвращает количество элементов, которые удалось прочитать.

size_t fwrite(const void *ptr, size_t size, size_t nelts, FILE *F);

Аналогично fread эта функция возвращает количество элементов, которые удалось записать.
Тут параметр nelts просто показывает, сколько элементов надо вывести.

1.6 Другие полезные опции: fseek и ftell

Для файлов, которые открыты на чтение есть полезные функции. Одна из них это:

int fseek(FILE *f, long offset, int flag);

  • FILE *f — файл, в котором передвигаемся;
  • long offset — количество байтов для отступа, отступ производится в соответствии с 3-м параметром;
  • int flag — позиция, от которой будет совершен отступ; в стандартной библиотеке C для этого параметра определены 3 константы:

SEEK_SET — начало файла;
SEEK_CUR — текущас позиция;
SEEK_END — конец файла;

long int ftell(FILE *f);

2 Другие подходы для работы с файлами

2.1 File descriptors. Open, close, read, write

В языке C есть много способов работы с файлами. Помимо структуры FILE можно использовать так называемые дескрипторы файла (file descriptors). Дескриптор файла — целое неотрицательное число. Оно обозначает номер открытого файла в таблице открытых файлов операционной системы. Использование дескрипторов файла — более низкий уровень, чем нежели ипользование струкруты FILE. Структура FILE — сущность языка C и его стандартной библиотеки, тогда как дескриптор файла — сущность операционной системы. Например, при работе со структурой FILE автоматически создается буфер, и программист работает с более высокоуровневой абстракцией. А при работе с дескрипторами файла программист должен позаботится о буферизации вручную.

Пример работы с дескрипторами файла довольно прост и почти в точности повторяет процесс работы со структурой FILE:

Сходство работы с дескрипторами файла с работой со структурой FILE заключается в том, что в названии функций отсутствует буква «f» . Иногда параметры функций незначительно отличаются.

Структуру FILE полезно использовать при работе с настоящими «файлами» (которые находятся на жестком диске). Ипользовать дескрипторы файла полезно в случаях работы со специальными «файлами». В этом подходе есть своя специфика работы, но сейчас просто полезно знать, что такой подход существует.

Аналогами stdin, stdout и stderr в дескрипторах файла являются числа 0, 1 и 2 соответственно. Стандарт POSIX.1 обозначил числа 0, 1, 2 символическими константами STDIN_FILENO, STDOUT_FILENO и STDERR_FILENO соответственно.

2.2 Memory mapping. Что делает mmap

Следующий способ работы с файлами удобен в тех случаях, когда приходится читать файл нелинейно: надо «ходить» вперед и назад. В предыдущих подходах такие ситуации оказывались неудобными с точки зрения программирования: получился бы громоздкий код.

В языке C был придуман удобный способ работы в таких ситуациях, который называется memory mapping. Соответствующая функция:

Работает эта функция примерно так. Мы указываем этой функции файл на диске, и она «отображает» этот файл в такую-то область в памяти. В результате работы функции мы получаем указатель на начало файла. И потом мы можем работать с этим файлом как с обычным указателем на какую-то область памяти: можем «ходить» вперед и назад по этому файлу.

Можно «отобразить» не весь файл целиком, а, например, отдельную часть файла: с 3-его килобайта по 4-ый килобайт.

2.3 Win32 API: FileCreate, FileRead , etc.

При работе с файлами в ОС Windows можно использовать все те функции, которые были описаны выше. В ОС Windows есть своя большая стандартная библиотека Win32 API . В этой библиотеке также есть функции для работы с файлами: например, функции FileCreate(. ) или FileOpen(. ) . Они по своей работе похожи на функции из стандартной библиотеки C, но отличия также присутствуют. Они заключаются в параметрах этих функций и небольших «хитростях», которые мы здесь опустим.

Если вы программируете под ОС Windows и пишите программу для работы в ОС Windows, то стоит пользоваться библиотекой Win32 API для работы с файлами.

3 Ввод и вывод в языке C++, потоки

В языке C++ объекты для работы с файлами называются потоками (streams). В данном случае слово «поток» означает то же самое, что и «файл» в языке C.

Классы для работы с файлами в языке C++ называются std::istream и std::ostream для ввода и вывода соответственно.

3.1 Глобальные переменные std::cout, std::cin, std::cerr

В header’е объявлена глобальная переменная std::cout; она используется как стандартный поток вывода на экран. Эта переменная является объектом класса std::ostream .
В этом классе есть перегруженный оператор

В данном случае на экран будет выведена единица.

Еще в header’е объявлены переменные std::cin и std::cerr для стандартного потока ввода и потока ошибок соответственно. Они являются объектами классов std::istream и std::ostream соотсветственно.

Аналогично тому, как stderr отличается от stdin , в языке C++ std::cerr отличается от std::cout отсутствием буферизации.

В классе std::istream есть перегруженный оператор >>. Можно считывать информацию из стандартного потока ввода (с клавиатуры).

3.2 Форматированный вывод возможен: std::ios::hex

Возможен ли форматированный вывод, которым мы пользовались в языке C фунцией printf() ? Наример, как вывести ту же переменную N в 16-ой записи?

В языке C++ форматированный вывод возможен при помощи вывода на экран специальной управляющей команды:

В точности то же самое выведет команда printf(«%x», N);

Чтобы не писать перед кажой переменной ее формат, можно использовать функцию:

Она установит формат вывода в стандартный поток вывода на экран. Этот подход настолько же мощный, как и использование форматной печати с помощью printf .

3.3 Операторы >

  • &os — поток, в который мы будем выводить;
  • N — переменная, которую мы будем выводить;
  • Оператор возвращает ссылку на поток, в результате чего можно писать так: std::cout

В случае оператора >> все аналогично.

Если у нас есть класс комплексных чисел Complex , то вывод этих чисел через оператор

3.4 Работа с файлами

Классами для работы с файлами в языке C++ являются ifstream, ofstream и fstream .

Код для открытия файла и его чтения выглядит примерно так:

ifstream ifs;
ifs.open(«file1.txt»);

// далее с помощью оператора >> можно читать из файла, если он успешно открылся;

Аналогично можно использовать конструктор с параметром: ifs(«file1.txt»); после чего создается объект и открывается по возможности файл.

В классе istream есть метод close() , который закрывает файл (на подобие работы с файлами в языке C). Однако вызывать этот метод необязательно. Дело в том, что в деструкторе класса этот метод вызовется автоматически.

Работа с объектами классов ofstream и ofstream и fstream осуществляется по аналогичному сценарию.

3.5 Класс stringstream

Класс stringstream наследуется от iostream .
Используется этот для класс для следующих целей. Если мы хотим выводить комплексные числа не только на экран или в файл, но и в окно какой-нибудь программы (GUI), то как использовать stringstream ? При этом мы не хотим писать один и тот же код программы.

Можно просто печатать в строку с помощью stringstream .

3.6 Иерархия классов

4 Общий совет

Общий совет заключается в том, что не надо смешивать техники для работы с файлами.
Например, не надо в одной и той же программе использовать функции из стандартной библиотеки C ( fread / fwrite ) и классы-потоки из языка C++ ( istream / ostream ).

Работа с текстовыми файлами

Теги: Текстовые файлы, fopen, fclose, feof, setbuf, setvbuf, fflush, fgetc, fprintf, fscanf, fgets, буферизированный поток, небуферизированный поток.

Работа с текстовыми файлами

Р абота с текстовым файлом похожа работу с консолью: с помощью функций форматированного ввода мы сохраняем данные в файл, с помощью функций форматированного вывода считываем данные из файла. Есть множество нюансов, которые мы позже рассмотрим. Основные операции, которые необходимо проделать, это

  • 1. Открыть файл, для того, чтобы к нему можно было обращаться. Соответственно, открывать можно для чтения, записи, чтения и записи, переписывания или записи в конец файла и т.п. Когда вы открываете файл, может также произойти куча ошибок – файла может не существовать, это может быть файл не того типа, у вас может не быть прав на работу с файлом и т.д. Всё это необходимо учитывать.
  • 2. Непосредственно работа с файлом — запись и чтение. Здесь также нужно помнить, что мы работаем не с памятью с произвольным доступом, а с буферизированным потоком, что добавляет свою специфику.
  • 3. Закрыть файл. Так как файл является внешним по отношению к программе ресурсом, то если его не закрыть, то он продолжит висеть в памяти, возможно, даже после закрытия программы (например, нельзя будет удалить открытый файл или внести изменения и т.п.). Кроме того, иногда необходимо не закрывать, а «переоткрывать» файл для того, чтобы, например, изменить режим доступа.

Кроме того, существует ряд задач, когда нам не нужно обращаться к содержимому файла: переименование, перемещение, копирование и т.д. К сожалению, в стандарте си нет описания функций для этих нужд. Они, безусловно, имеются для каждой из реализаций компилятора. Считывание содержимого каталога (папки, директории) – это тоже обращение к файлу, потому что папка сама по себе является файлом с метаинформацией.

Иногда необходимо выполнять некоторые вспомогательные операции: переместиться в нужное место файла, запомнить текущее положение, определить длину файла и т.д.

Для работы с файлом необходим объект FILE. Этот объект хранит идентификатор файлового потока и информацию, которая нужна, чтобы им управлять, включая указатель на его буфер, индикатор позиции в файле и индикаторы состояния.

Объект FILE сам по себе является структурой, но к его полям не должно быть доступа. Переносимая программа должна работать с файлом как с абстрактным объектом, позволяющим получить доступ до файлового потока.

Создание и выделение памяти под объект типа FILE осуществляется с помощью функции fopen или tmpfile (есть и другие, но мы остановимся только на этих).

Функция fopen открывает файл. Она получает два аргумента – строку с адресом файла и строку с режимом доступа к файлу. Имя файла может быть как абсолютным, так и относительным. fopen возвращает указатель на объект FILE, с помощью которого далее можно осуществлять доступ к файлу.

FILE* fopen(const char* filename, const char* mode);

Например, откроем файл и запишем в него Hello World

#include #include #include void main() < //С помощью переменной file будем осуществлять доступ к файлу FILE *file; //Открываем текстовый файл с правами на запись file = fopen("C:/c/test.txt", "w+t"); //Пишем в файл fprintf(file, "Hello, World!"); //Закрываем файл fclose(file); getch(); >

Функция fopen сама выделяет память под объект, очистка проводится функцией fclose. Закрывать файл обязательно, самостоятельно он не закроется.

Функция fopen может открывать файл в текстовом или бинарном режиме. По умолчанию используется текстовый. Режим доступа может быть следующим

Параметры доступа к файлу.

Тип Описание
r Чтение. Файл должен существовать.
w Запись нового файла. Если файл с таким именем уже существует, то его содержимое будет потеряно.
a Запись в конец файла. Операции позиционирования (fseek, fsetpos, frewind) игнорируются. Файл создаётся, если не существовал.
r+ Чтение и обновление. Можно как читать, так и писать. Файл должен существовать.
w+ Запись и обновление. Создаётся новый файл. Если файл с таким именем уже существует, то его содержимое будет потеряно. Можно как писать, так и читать.
a+ Запись в конец и обновление. Операции позиционирования работают только для чтения, для записи игнорируются. Если файл не существовал, то будет создан новый.

Если необходимо открыть файл в бинарном режиме, то в конец строки добавляется буква b, например “rb”, “wb”, “ab”, или, для смешанного режима “ab+”, “wb+”, “ab+”. Вместо b можно добавлять букву t, тогда файл будет открываться в текстовом режиме. Это зависит от реализации. В новом стандарте си (2011) буква x означает, что функция fopen должна завершиться с ошибкой, если файл уже существует. Дополним нашу старую программу: заново откроем файл и считаем, что мы туда записали.

#include #include #include void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); fclose(file); file = fopen("C:/c/test.txt", "r"); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

Вместо функции fgets можно было использовать fscanf, но нужно помнить, что она может считать строку только до первого пробела.
fscanf(file, «%127s», buffer);

Также, вместо того, чтобы открывать и закрывать файл можно воспользоваться функцией freopen, которая «переоткрывает» файл с новыми правами доступа.

#include #include #include void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); freopen("C:/c/test.txt", "r", file); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

Функции fprintf и fscanf отличаются от printf и scanf только тем, что принимают в качестве первого аргумента указатель на FILE, в который они будут выводить или из которого они будут читать данные. Здесь стоит сразу же добавить, что функции printf и scanf могут быть без проблем заменены функциями fprintf и fscanf. В ОС (мы рассматриваем самые распространённые и адекватные операционные системы) существует три стандартных потока: стандартный поток вывода stdout, стандартный поток ввода stdin и стандартный поток вывода ошибок stderr. Они автоматически открываются во время запуска приложения и связаны с консолью. Пример

#include #include #include void main() < int a, b; fprintf(stdout, "Enter two numbers\n"); fscanf(stdin, "%d", &a); fscanf(stdin, "%d", &b); if (b == 0) < fprintf(stderr, "Error: divide by zero"); >else < fprintf(stdout, "%.3f", (float) a / (float) b); >getch(); >

Ошибка открытия файла

Если вызов функции fopen прошёл неудачно, то она возвратит NULL. Ошибки во время работы с файлами встречаются достаточно часто, поэтому каждый раз, когда мы окрываем файл, необходимо проверять результат работы

#include #include #include #define ERROR_OPEN_FILE -3 void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); if (file == NULL) < printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); >fprintf(file, "Hello, World!"); freopen("C:/c/test.txt", "r", file); if (file == NULL) < printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); >fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

Проблему вызывает случай, когда открывается сразу несколько файлов: если один из них нельзя открыть, то остальные также должны быть закрыты

. FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if (inputFile == NULL) < printf("Error opening file %s", INPUT_FILE); getch(); exit(3); >outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) < printf("Error opening file %s", OUTPUT_FILE); getch(); if (inputFile != NULL) < fclose(inputFile); >exit(4); > .

В простых случаях можно действовать влоб, как в предыдущем куске кода. В более сложных случаях используются методы, подменяющиее RAII из С++: обёртки, или особенности компилятора (cleanup в GCC) и т.п.

Буферизация данных

  • 1) Если он заполнен
  • 2) Если поток закрывается
  • 3) Если мы явно указываем, что необходимо очистить буфер (здесь тоже есть исключения:)).
  • 4) Также очищается, если программа завершилась удачно. Вместе с этим закрываются и все файлы. В случае ошибки выполнения этого может не произойти.

Форсировать выгрузку буфера можно с помощью вызова функции fflush(File *). Рассмотрим два примера – с очисткой и без.

#include #include #include void main() < FILE *file; char c; file = fopen("C:/c/test.txt", "w"); do < c = getch(); fprintf(file, "%c", c); fprintf(stdout, "%c", c); //fflush(file); >while(c != 'q'); fclose(file); getch(); >

Раскомментируйте вызов fflush. Во время выполнения откройте текстовый файл и посмотрите на поведение.

Буфер файла можно назначить самостоятельно, задав свой размер. Делается это при помощи функции

void setbuf (FILE * stream, char * buffer);

которая принимает уже открытый FILE и указатель на новый буфер. Размер нового буфера должен быть не меньше чем BUFSIZ (к примеру, на текущей рабочей станции BUFSIZ равен 512 байт). Если передать в качестве буфера NULL, то поток станет небуферизированным. Можно также воспользоваться функцией

int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );
  • _IOFBF — полная буферизация. Данные записываются в файл, когда он заполняется. На считывание, буфер считается заполненным, когда запрашивается операция ввода и буфер пуст.
  • _IOLBF — линейная буферизация. Данные записываются в файл когда он заполняется, либо когда встречается символ новой строки. На считывание, буфер заполняется до символа новой строки, когда запрашивается операция ввода и буфер пуст.
  • _IONBF – без буферизации. В этом случае параметры size и buffer игнорируются.

Пример: зададим свой буфер и посмотрим, как осуществляется чтение из файла. Пусть файл короткий (что-нибудь, типа Hello, World!), и считываем мы его посимвольно

#include #include #include void main() < FILE *input = NULL; char c; char buffer[BUFSIZ * 2] = ; input = fopen("D:/c/text.txt", "rt"); setbuf(input, buffer); while (!feof(input)) < c = fgetc(input); printf("%c\n", c); printf("%s\n", buffer); _getch(); >fclose(input); >

Видно, что данные уже находятся в буфере. Считывание посимвольно производится уже из буфера.

feof

Функция int feof (FILE * stream); возвращает истину, если конец файла достигнут. Функцию удобно использовать, когда необходимо пройти весь файл от начала до конца. Пусть есть файл с текстовым содержимым text.txt. Считаем посимвольно файл и выведем на экран.

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (!feof(input)) < c = fgetc(input); fprintf(stdout, "%c", c); >fclose(input); _getch(); >

Всё бы ничего, только функция feof работает неправильно. Это связано с тем, что понятие «конец файла» не определено. При использовании feof часто возникает ошибка, когда последние считанные данные выводятся два раза. Это связано с тем, что данные записывается в буфер ввода, последнее считывание происходит с ошибкой и функция возвращает старое считанное значение.

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (!feof(input)) < fscanf(input, "%c", &c); fprintf(stdout, "%c", c); >fclose(input); _getch(); >

Этот пример сработает с ошибкой (скорее всего) и выведет последний символ файла два раза.

Решение – не использовать feof. Например, хранить общее количество записей или использовать тот факт, что функции fscanf и пр. обычно возвращают число верно считанных и сопоставленных значений.

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (fscanf(input, "%c", &c) == 1) < fprintf(stdout, "%c", c); >fclose(input); _getch(); >

Примеры

1. В одном файле записаны два числа — размерности массива. Заполним второй файл массивом случайных чисел.

#include #include #include #include //Имена файлов и права доступа #define INPUT_FILE "D:/c/input.txt" #define OUTPUT_FILE "D:/c/output.txt" #define READ_ONLY "r" #define WRITE_ONLY "w" //Максимальное значение для размера массива #define MAX_DIMENSION 100 //Ошибка при открытии файла #define ERROR_OPEN_FILE -3 void main() < FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if (inputFile == NULL) < printf("Error opening file %s", INPUT_FILE); getch(); exit(ERROR_OPEN_FILE); >outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) < printf("Error opening file %s", OUTPUT_FILE); getch(); //Если файл для чтения удалось открыть, то его необходимо закрыть if (inputFile != NULL) < fclose(inputFile); >exit(ERROR_OPEN_FILE); > fscanf(inputFile, "%ud %ud", &m, &n); if (m > MAX_DIMENSION) < m = MAX_DIMENSION; >if (n > MAX_DIMENSION) < n = MAX_DIMENSION; >srand(time(NULL)); for (i = 0; i < n; i++) < for (j = 0; j < m; j++) < fprintf(outputFile, "%8d ", rand()); >fprintf(outputFile, "\n"); > //Закрываем файлы fclose(inputFile); fclose(outputFile); >

2. Пользователь копирует файл, при этом сначала выбирает режим работы: файл может выводиться как на консоль, так и копироваться в новый файл.

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *origin = NULL; FILE *output = NULL; char filename[1024]; int mode; printf("Enter filename: "); scanf("%1023s", filename); origin = fopen(filename, "r"); if (origin == NULL) < printf("Error opening file %s", filename); getch(); exit(ERROR_FILE_OPEN); >printf("enter mode: [1 - copy, 2 - print] "); scanf("%d", &mode); if (mode == 1) < printf("Enter filename: "); scanf("%1023s", filename); output = fopen(filename, "w"); if (output == NULL) < printf("Error opening file %s", filename); getch(); fclose(origin); exit(ERROR_FILE_OPEN); >> else < output = stdout; >while (!feof(origin)) < fprintf(output, "%c", fgetc(origin)); >fclose(origin); fclose(output); getch(); >

3. Пользователь вводит данные с консоли и они записываются в файл до тех пор, пока не будет нажата клавиша esc. Проверьте программу и посмотрите. как она себя ведёт в случае, если вы вводите backspace: что выводится в файл и что выводится на консоль.

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *output = NULL; char c; output = fopen("D:/c/test_output.txt", "w+t"); if (output == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >for (;;) < c = _getch(); if (c == 27) < break; >fputc(c, output); fputc(c, stdout); > fclose(output); >

4. В файле записаны целые числа. Найти максимальное из них. Воспользуемся тем, что функция fscanf возвращает число верно прочитанных и сопоставленных объектов. Каждый раз должно возвращаться число 1.

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >maxn = INT_MIN; hasRead = 1; while (hasRead == 1) < hasRead = fscanf(input, "%d", &num); if (hasRead != 1) < continue; >if (num > maxn) < maxn = num; >> printf("max number = %d", maxn); fclose(input); _getch(); >

Другое решение считывать числа, пока не дойдём до конца файла.

#include #include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >maxn = INT_MIN; while (!feof(input)) < fscanf(input, "%d", &num); if (num >maxn) < maxn = num; >> printf("max number = %d", maxn); fclose(input); _getch(); >

5. В файле записаны слова: русское слово, табуляция, английское слово, в несколько рядов. Пользователь вводит английское слово, необходимо вывести русское.

Файл с переводом выглядит примерно так

солнце sun
карандаш pen
шариковая ручка pencil
дверь door
окно windows
стул chair
кресло armchair

и сохранён в кодировке cp866 (OEM 866). При этом важно: последняя пара cлов также заканчивается переводом строки.

Алгоритм следующий — считываем строку из файла, находим в строке знак табуляции, подменяем знак табуляции нулём, копируем русское слово из буфера, копируем английское слово из буфера, проверяем на равенство.

#include #include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; char buffer[512]; char enWord[128]; char ruWord[128]; char usrWord[128]; unsigned index; int length; int wasFound; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >printf("enter word: "); fgets(usrWord, 127, stdin); wasFound = 0; while (!feof(input)) < fgets(buffer, 511, input); length = strlen(buffer); for (index = 0; index < length; index++) < if (buffer[index] == '\t') < buffer[index] = '\0'; break; >> strcpy(ruWord, buffer); strcpy(enWord, &buffer[index + 1]); if (!strcmp(enWord, usrWord)) < wasFound = 1; break; >> if (wasFound) < printf("%s", ruWord); >else < printf("Word not found"); >fclose(input); _getch(); >

6. Подсчитать количество строк в файле. Будем считывать файл посимвольно, считая количество символов ‘\n’ до тех пор, пока не встретим символ EOF. EOF – это спецсимвол, который указывает на то, что ввод закончен и больше нет данных для чтения. Функция возвращает отрицательное значение в случае ошибки.
ЗАМЕЧАНИЕ: EOF имеет тип int, поэтому нужно использовать int для считывания символов. Кроме того, значение EOF не определено стандартом.

#define _CRT_SECURE_NO_WARNINGS #include #include #include int cntLines(const char *filename) < int lines = 0; int any; //any типа int, потому что EOF имеет тип int! FILE *f = fopen(filename, "r"); if (f == NULL) < return -1; >do < any = fgetc(f); //printf("%c", any);//debug if (any == '\n') < lines++; >> while(any != EOF); fclose(f); return lines; > void main() < printf("%d\n", cntLines("C:/c/file.txt")); _getch(); >

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Как задать текстовый файл в c

Язык C позволяет осуществлять различные операции с файлами. В большинстве случаев эти операции не связаны с какими-либо сложностями.

Открытие файлов

FILE *fp;
fp = fopen(filename, mode);

Параметр filename позволяет передать имя файла (при отсутствии имени файла функция вернет NULL). Параметр mode позволяет указать режим открытия файла. Существуют следующие режимы открытия файлов:

  • r — открытие файла для чтения с позиционированием указателя в начале файла.
  • r+ — открытие файла для чтения и записи с позиционированием указателя в начале файла.
  • w — удаление содержимого существующего файла или создание нового файла для последующей записи с позиционированием указателя в начале файла.
  • w+ — удаление содержимого существующего файла или создание нового файла для последующего чтения и записи с позиционированием указателя в начале файла.
  • a — создание или открытие существующего файла для дополнения с позиционированием указателя в конце файла.
  • a+ — создание или открытие существующего файла для чтения и дополнения с позиционированием указателя в начале файла для чтения и в конце файла для дополнения.
Закрытие файлов

Закрытие файла должно осуществляться сразу же после окончания работы с ним для предотвращения потери данных. Следует убедиться в том, что содержимое файла не модифицируется после его закрытия.

Чтение данных из файлов

Для чтения данных из файла следует создать буфер (или буферы), в который будут помещаться прочитанные данные, целочисленную переменную, в которой будет сохраняться возвращаемое функцией количество прочитанных байтов и воспользоваться функцией fread().

size_t bytesread;
char buffer[100];
bytesread = fread(buffer, sizeof(buffer), 1, fp);

В качестве первого параметра используется указатель на буфер, в качестве второго — размер этого буфера, в качестве третьего — количество буферов для чтения, а в качестве четвертого — открытый файл. При достижении конца файла функция будет возвращать, как и в случае ошибки, нулевое значение, поэтому успешное достижение конца файла может быть установлено с помощью feof(), а возникновение ошибки — с помощью функции ferror().

Для чтения строк из текстового файла может использоваться функция fgets(). При работе с ней также следует использовать буфер для сохранения прочитанной строки.

char buffer[100];
fgets(buffer, sizeof(buffer), fp);

В качестве первого параметра используется указатель на буфер, в качестве второго — размер этого буфера, в качестве третьего — открытый файл. Функция возвращает NULL в случае ошибки или достижения конца файла.

Запись данных в файлы

Для записи данных в файл следует подготовить буфер (или буферы) с данными для записи, целочисленную переменную, в которой будет сохраняться возвращаемое функцией количество записанных байтов и воспользоваться функцией fwrite().

size_t byteswritten;
char buffer[100];
byteswritten = fread(buffer, sizeof(buffer), 1, fp);

В качестве первого параметра используется указатель на буфер, в качестве второго — размер этого буфера, в качестве третьего — количество буферов для записи, а в качестве четвертого — открытый файл. При возникновении ошибки функция будет возвращать нулевое значение.

Для записи строк в текстовый файл может использоваться функция fputs(). При работе с ней также следует подготовить буфер со строкой для записи.

char buffer[100];
fputs(buffer, sizeof(buffer), fp);

Функция возвращает количество записанных байтов или символ EOF в случае возникновения ошибки.

Переименование файлов

Файлы могут свободно переименовываться. Для этого достаточно задать текущее и новое имена интересующего файла. Функция rename() возвращает нулевое значение при удачном переименовании файла.

res = rename(old, new);

С помощью первого параметра передается текущее имя файла, с помощью второго — его новое имя.

Удаление файлов

Файлы также могут удаляться. Для этого достаточно задать текущее имя интересующего файла. Функция remove() возвращает нулевое значение при удачном удалении файла.

С помощью единственного параметра передается имя существующего файла.

Права доступа

Каждый файл имеет права доступа, которые могут изменяться его владельцем при необходимости. В некоторых случаях файл может создаваться с правами только для чтения, что позволит предотвратить его изменение или с правами для исполнения, что позволит исполнять его как программу.

Функция chmod() позволяет устанавливать права доступа к файлу.

Параметр filename позволяет передать имя файла, права доступа к которому необходимо изменить. Параметр mode позволяет указать права доступа к файлу в восьмеричном формате.

Пример

Данная простая программа построчно читает содержимое файла из виртуальной файловой файловой системы /proc и выводит информацию об установленном в компьютере центральном процессоре.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *