Справочник от Автор24
Поделись лекцией за скидку на Автор24

Определение, открытие, закрытие, ввод-вывод файла. Произвольный доступ к файлу. Работа с текстовыми файлами. Библиотечные функции для работы с файлами

  • 👀 339 просмотров
  • 📌 287 загрузок
Выбери формат для чтения
Загружаем конспект в формате rtf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Определение, открытие, закрытие, ввод-вывод файла. Произвольный доступ к файлу. Работа с текстовыми файлами. Библиотечные функции для работы с файлами» rtf
Раздел 6. Файлы Определение, открытие, закрытие, ввод-вывод файла. Произвольный доступ к файлу. Работа с текстовыми файлами. Библиотечные функции для работы с файлами. Файлом называют способ хранения информации на физическом устройстве. Файл — это понятие, которое применимо ко всему - от файла на диске до терминала. Все файлы рассматриваются как последовательность байтов, заканчивающаяся особым кодом, обозначающим их окончание (End Of File). Все необходимые действия выполняются с помощью функций, включенных в стандартную библиотеку. Они позволяют работать с различными устройствами, такими, как диски, принтер, коммуникационные каналы и т.д. Функции ввода и вывода в стандартной библиотеке Си позволяют читать данные из файлов или получать их с устройств ввода (например, с клавиатуры) и записывать данные в файлы, или выводить их на различные устройства (например, на принтер). Эти устройства сильно отличаются друг от друга. Однако файловая система преобразует их в единое абстрактное логическое устройство, называемое потоком. Функции языка С позволяют осуществлять: - ввод/вывод верхнего уровня (потоковый ввод-вывод) - ввод/вывод нижнего уровня (с использованием понятия "дескриптор"). При потоковом вводе-выводе обмен данными производится побайтно, но возможно работать с данными различных размеров и форматов посредством организации буферизованного потока. Таким образом, можно сказать, что поток – это файл вместе со средствами буферизации. Для работы с потоком предусмотрен ряд функций, описанных в заголовочном файле stdio.h. Итак, функции ввода/вывода верхнего уровня обеспечивают буферизацию работы с файлами. Это означает, что, когда производится чтение информации из файла или запись информации в файл, обмен информацией осуществляется не между программой и указанным файлом, а между программой и промежуточным буфером, расположенным в оперативной памяти. В буфер информация считывается из файла при открытии файла. Для пользователя файл, открытый на верхнем уровне, представляется как последовательность считываемых или записываемых байтов. Чтобы отразить эту особенность организации ввода/вывода, предложено понятие "поток" (stream). Когда файл открывается, с ним связывается поток, выводимая информация записывается "в поток", считываемая информация берется "из потока". Когда поток открывается для ввода/вывода, он связывается со структурой типа FILE (имя типа FILE определяется с помощью конструкции typedef в файле stdio.h). Структура содержит разнообразную информацию о файле, , при помощи которой осуществляется работа с потоком. При открытии файла с помощью функции fopen возвращается указатель на поток, т.е. на структуру типа FILE. Указатель, связывающий исполняемую программу с файлом, должен быть объявлен в виде FILE *имя_потока; Этот указатель (указатель потока) используется для последующих операций с файлом, пользователь не обязан вникать в способ организации потока, он только должен сохранить полученный указатель и передавать его значение всем библиотечным функциям, используемым для ввода/вывода через этот поток. Функции в/в верхнего уровня относятся к числу функций, одинаково реализуемых в различных ОС и на разных компьютерах, с их помощью пользователь имеет возможность писать переносимые программы. Работа с потоком В Си существует два типа потоков: текстовые (text) и двоичные (binary). Текстовый поток — это последовательность символов. При передаче символов из потока на экран, часть из них не выводится (например, символ возврата каретки, перевода строки). Двоичный (бинарный) поток — это последовательность байтов, которые однозначно соответствуют тому, что находится на внешнем устройстве. Вся работа с файлом выполняется через файловую переменную-указатель на структуру типа FILE, определённую в стандартной библиотеке. Его необходимо определить; делается это, например, так: FILE *fp; Открыть файл можно функцией fopen, имеющей два параметра: FILE *fopen (char *имя_файла, char *режим_доступа) При успешном открытии файла указатель текущей позиции в файле устанавливается перед первым элементом файла. Параметр <имя_файла> может содержать относительный или абсолютный путь к открываемому файлу. Приведём примеры его значений: 1. "data.txt" – открывается файл с именем data.txt из текущей папки. Следует понимать, что, при запуске исполняемого файла приложения не из VS, текущая папка – та, где находится исполняемый файл. При отладке в VS папка может быть иной; 2. "f:\\my.dat" – открывается файл my.dat из головной папки диска f: . Так как обратный слеш '\' в C/C++ является служебным символом, при записи пути к файлу его нужно удваивать 3. Имя файла также может быть запрошено у пользователя: char namef[80]; printf ("\nВведите имя файла:"); gets (namef); Спецификация файла (т.е. имя файла и путь к нему) может, например, иметь вид: "c:\\my_prog.txt" - для файла my_prog.txt на диске с: . Параметр <режим_доступа> (cпособ использования файла) определяет, какие действия будут разрешены с открываемым файлом. Примеры его возможных значений: r - открыть существующий файл для чтения; w - создать новый файл для записи (если файл с указанным именем существует, то он будет переписан); а - дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, или создать файл, если он не существует); Возможны два режима обмена информацией с файлом: 1. текстовый и 2. бинарный (двоичный). При работе с файлом в текстовом режиме содержимое файлов разбивается на строки. В конце каждой строки записывается пара кодов для обозначения конца строки (CR и LF), получаемых преобразованием символа окончания строки '\n', а при чтении данных из файла производится обратное преобразование кодов CR и LF в '\n'. При работе с файлом в бинарном режиме средства буферизации отсутствуют. Информация считывается непосредственно в таком виде, в каком она хранится в файле. Никакие ее дополнительные преобразования не выполняются. В этом режиме работы с файлом информация побайтно передается в переменные из файла по указанным адресам и таким же образом считывается из них и записывается в файл. Вся информация воспринимаются равнозначной. Режимы открытия файла (параметр <режим_доступа>): b - файл открывается в двоичном режиме t - файл открывается в текстовом режиме r+ - открыть существующий файл для чтения и записи; w+ - создать новый файл для чтения и записи; a+ - дополнить или создать файл с возможностью чтения и записи; rb - открыть двоичный файл для чтения; wb - создать двоичный файл для записи; аb - дополнить двоичный файл; r+b - открыть двоичный файл для чтения и записи; w+b - создать двоичный файл для чтения и записи; а+b - дополнить двоичный файл с предоставлением возможности чтения и записи; rt - открыть текстовой файл для чтения; wt - создать текстовый файл для записи; at - дополнить текстовый файл; r+t - открыть текстовой файл для чтения и записи; w+t - создать текстовый файл для чтения записи; a+t - дополнить текстовый файл с предоставлением возможности записи и чтения. Строки вида r+b можно записывать и в другой форме: rb+. По факту указание режима доступа "b" или "t" не накладывает каких-либо ограничений на методы, которые будут применяться для чтения или записи данных. После открытия файла следует обязательно проверить, удалась ли эта операция. Для этого нужно сравнить указатель, который вернула fopen, с константой NULL (nullptr) стандартной библиотеки. Рекомендуется использовать следующий способ открытия файла: if ((fp = fopen ("c:\\my_prog.txt", "rt")) == NULL) { /* Если в результате обращения к функции fopen( ) возникает ошибка, то она возвращает указатель на константу NULL. */ puts("Открыть файл не удалось\n"); exit(1); } Пример 1. Приложение проверяет, удалось ли открыть файл из текущей папки, имя файла запрашивается у пользователя. int main(void) { FILE *fp; char namef [80]; printf ("\n Введите имя файла:"); gets (namef); fp = fopen (namef, "r+b"); if (fp==NULL) { printf ("\n не удалось открыть файл"); getchar(); exit (1); //Выйти с кодом завершения 1 } getchar(); return 0; } Функции, возвращающие указатель, в том числе fopen, считаются небезопасными в ряде новых компиляторов, например с Visual Studio 2015 и выше. Если их использование приводит не к предупреждению, а к генерации ошибок, есть два основных способа решения проблемы: 1. В языке C++ существует две версии библиотеки iostream: библиотека старого стиля, определенная ранними версиями C++, и библиотека, определенная стандартом ANSI/ISO для языка C++(Standard C++), в которой реализован новый подход к программированию. Поэтому в соответствии с рекомендациями компилятора, следует заменить старые названия функций на их безопасные версии, например strcpy на strcpy_s и fopen на fopen_s. Т.к. функции, возвращающие указатель, считаются небезопасными, отсюда и изменившийся шаблон метода открытия файла. При этом может измениться и способ вызова функций, например, FILE *out; fopen_s (&out,"data.txt", "wt"); вместо FILE *out = fopen_s ("data.txt", "wt"); 2. В начале файла (до всех #include) указать директиву #define _CRT_SECURE_NO_WARNINGS Если используется предкомпиляция, то можно определить этот макрос в заголовочном файле stdafx.h. Выбор способа чтения или записи данных зависит от того, какой должна быть структура файла. Если файл форматированный, т.е. является текстовым и состоит из лексем, разделённых стандартными разделителями (пробел, табуляция, перевод строки), обмен данными с ним можно выполнять следующими методами: -- fscanf – для чтения; -- fprintf – для записи. Первым параметром этих функций указывается файловая переменная, в остальном работа совпадает со стандартными scanf и printf. Функция fscanf() аналогична по смыслу функции scanf(), но в отличие от нее осуществляет форматированный ввод (чтение) из файла, а не стандартного потока ввода. int fscanf (FILE * stream, char * format [, adress,...]); Функция fscanf() принимает параметры: файловый указатель, строку формата, адреса областей памяти для записи данных: fscanf (myfile, "%s%d", str, &a); Функция fscanf() возвращает количество удачно считанных данных или при достижении конца файла EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных. Библиотечные функции, используемые для работы с файлами ( stdio.h): 1. Функция putc( ) записывает символ в файл и имеет следующий прототип: int putc (int с, FILE *fp); Здесь fp - указатель на файл, возвращенный функцией fopen( ), с - символ для записи (переменная с имеет тип int, но используется только младший байт). При успешном завершении putc( ) возвращает записанный символ, в противном случае возвращается константа EOF. Она определена в файле stdio.h и имеет значение (-1). 2. Функция getc( ) читает символ из файла и имеет следующий прототип: int getc(FILE *fp); Здесь fp - указатель на файл, возвращенный функцией fopen( ). Эта функция возвращает прочитанный символ. Соответствующее значение имеет int, но старший байт равен нулю. Если достигнут конец файла, то getc возвращает значение ЕОF. 3. Функция feof( ) определяет конец файла при чтении двоичных данных и имеет следующий прототип: int feof(FILE *fp); Здесь fp - указатель на файл, возвращенный функцией fopen( ). При достижении конца файла возвращается ненулевое значение, иначе возвращается 0. 4. Функция fputs( ) записывает строку символов в файл. Она отличается от функции puts( ) только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа. Например: fputs("Ехаmple", fp); При возникновении ошибки возвращается значение EOF. 5. Функция fgets( ) читает строку символов из файла. Она отличается от функции gets( ) тем, что в качестве второго параметра нужно указать максимальное число вводимых символов плюс единица, а в качестве третьего - указатель на переменную файлового типа. Строка считывается целиком, если ее длина не превышает указанного числа символов, в противном случае функция возвращает только заданное число символов. Рассмотрим пример: fgets(string, n, fp); Функция возвращает указатель на строку string при успешном завершении и константу NULL в случае ошибки либо достижения конца файла. 6. Функция fprintf( ) выполняет те же действия, что и функция printf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа. Например: fprintf(fp, "%х", а); 7. Функция fscanf( ) выполняет те же действия, что и функция scanf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа. Например: fscanf(fp, "%х", &a); При достижении конца файла возвращается значение EOF. 8. Функция fseek( ) позволяет выполнять чтение и запись с произвольным доступом и имеет следующий прототип: int fseek (FILE *fp, long count, int access); Здесь fp - указатель на файл, возвращенный функцией fopen( ), count - номер байта относительно заданной начальной позиции, начиная с которого будет выполняться операция, access - способ задания начальной позиции. Переменная access может принимать следующие значения: 0 - начальная позиция задана в начале файла; 1 - начальная позиция считается текущей; 2 - начальная позиция задана в конце файла. При успешном завершении возвращается нуль, при ошибке - ненулевое значение. 9. Функция ferror( ) позволяет проверить правильность выполнения последней операции при работе с файлами. Имеет следующий прототип: int ferror(FILE *fp); В случае ошибки возвращается ненулевое значение, в противном случае возвращается нуль. 10. Функция remove( ) удаляет файл и имеет следующий прототип: int remove (char *file_name); Здесь file_name - указатель на строку со спецификацией файла. При успешном завершении возвращается нуль, в противном случае возвращается ненулевое значение. 11. Функция rewind( ) устанавливает указатель текущей позиции в начало файла и имеет следующий прототип: void rewind(FILE *fp); 12. Функция fread( ) предназначена для чтения блоков данных из потока. Имеет прототип: unsigned fread (void *ptr, unsigned size, unsigned n, FILE *fp); Она читает n элементов данных, длиной size байт каждый, из заданного входного потока fp в блок, на который указывает указатель ptr. Общее число прочитанных байтов равно произведению n*size. При успешном завершении функция fread( ) возвращает число прочитанных элементов данных, при ошибке - 0. 13. Функция fwrite( ) предназначена для записи в файл блоков данных. Имеет прототип: unsigned fwrite (void *ptr, unsigned size, unsigned n, FILE *fp); Она добавляет n элементов данных, длиной size байт каждый, в заданный выходной файл fp. Данные записываются с позиции, на которую указывает указатель ptr. При успешном завершении операции функция fwrite( ) возвращает число записанных элементов данных, при ошибке - неверное число элементов данных. Работа с текстовыми файлами Пример 2. Файл text.txt в текущей папке приложения имеет следующий вид: 1 1.5 -3.5 2 3.5 Прочитаем его как последовательность вещественных чисел. FILE *fp = fopen ("text.txt", "r"); if (fp==NULL) { printf ("\n не удалось открыть файл"); getchar (); exit (1); } float a; while (1) { fscanf (fp, "%f", &a); if (feof(fp)) break; //обработка очередного значения a printf ("%.2f ",a); } fclose(fp); После окончания работы с файлом он должен быть закрыт. Это делается с помощью библиотечной функции fclose(). Она имеет следующий прототип: int fclose(FILE *fp); При успешном завершении операции функция fclose( ) возвращает значение нуль. Любое другое значение говорит об ошибке. Если необходимо завершить работу с несколькими файлами, можно использовать функцию: int fcloseall(void); При работе с текстовыми файлами важно учитывать следующие моменты (см. пример 2): 1. Функции семейства scanf возвращают целое число – количество значений, которые успешно прочитаны в соответствии с указанным форматом. В реальных приложениях эту величину следует проверять в коде: int i=fscanf (fp, "%f", &a); if (i!=1) { //не удалось получить одно значение } 2. На "восприятие" программой данных может влиять установленная в приложении локаль. Например, если до показанного кода выполнен оператор setlocale(LC_ALL, "Rus"); результат работы кода может измениться (для русской локали разделителем целой и дробной части числа является запятая, а не точка). 3. Очередное чтение данных изменяет внутренний файловый указатель. Этот указатель в любой момент времени, пока файл открыт, показывает на следующее значение, которое будет прочитано. Благодаря этому наш код с бесконечным while не зациклится. 4. Код показывает, как читать из файла заранее неизвестное количество значений. Это позволяет сделать стандартная функция feof (проверка, достигнут ли конец файла; вернёт не ноль, если прочитано всё). 5. Распространённый в примерах из Интернета код вида while (! feof (fp)) { fscanf (fp, "%f", &a); //обработка числа a } под Windows может породить неточности при интерпретации данных. Например, этот код может прочитать как последнее значение завершающий перевод строки в файле, благодаря чему последнее прочитанное значение удвоится. Пример 3. В качестве примера записи в текстовый файл сохраним массив a из 10 целочисленных значений в файле с именем result.txt по 5 элементов в строке: const int n=10; int a[n], i; FILE *fp=fopen ("result.txt", "wt"); if (fp==NULL) { puts ("Не удалось открыть файл!"); getchar (); exit (1); } for (i=0; i #include #include int main () { FILE *file; struct food // объявляется структура { char name [20]; unsigned qty; float price; }; struct food shop [10]; // объявляется массив структур char i=0;   file = fopen ("fscanf.txt", "r"); /* Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля.*/   while (fscanf (file, "%s%u%f", shop[i].name, &(shop[i]. qty), &(shop[i]. price)) != EOF) { // За одну итерацию цикл считывает одну строку printf ("%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; /* Когда встречается конец файла, fscanf() возвращает значение EOF, и цикл завершается */ } printf("\n"); fclose(file); system("pause"); } } Чтение из текстового файла fgets() Функция fgets() осуществляет построчный ввод из файла. Один очередной вызов fgets() позволят прочитать одну строку. При этом можно прочитать не всю строку, а лишь ее часть от начала. Функция возвращает указатель на строку string при успешном завершении и константу NULL в случае ошибки либо достижения конца файла. Параметры fgets() выглядят таким образом: char * fgets (char * string, int num, FILE * filestream ); Или: fgets(массив_символов, количество_считываемых_символов, указатель_на_файл) Например: FILE *myfile; fgets (str, 50, myfile) Такой вызов функции прочитает из файла, связанного с указателем myfile, одну строку текста полностью, если ее длина меньше 50 символов с учетом символа '\n', который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ '\0', добавленный fgets(). Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет '\0'. В таком случае '\n' в считанной строке содержаться не будет. #define _CRT_SECURE_NO_WARNINGS #include #include #include #define N 80 int main () { FILE *file; char arr[N];   file = fopen ("fscanf.txt", "r");   while (fgets (arr, N, file) != NULL) printf ("%s", arr);   printf("\n"); fclose(file); system("pause"); } В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr. Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку. getc() или fgetc() Теперь рассмотрим текстовый файл, состоящий из неструктурированных строк (абзацев) текста, разделённых символами перевода строки. При работе с такими данными могут потребоваться следующие функции: 1. fgetc и fputc – для посимвольного чтения и посимвольной записи данных; 2. fgets и fputs – для чтения и записи строк с указанным максимальным размером. Как и в случае с функциями для чтения форматированных данных, у всех этих методов имеются аналоги для работы со стандартным вводом/выводом. Функция getc() или fgetc() (работает и то и другое) позволяет получить из файла очередной один символ. Соответствующее значение имеет int, но старший байт равен нулю. Если достигнут конец файла, то getc возвращает значение ЕОF. Пример 4. Читая файл, определить длину каждой строки в символах. Для решения задачи воспользуемся тем фактом, что строки завершаются символом "перевод строки" ('\n'). Пусть файл уже открыт для чтения. int c; int len=0, cnt=0; while (1) { c=fgetc(fp); if (c=='\n') { printf ("\n String %d, len=%d", ++cnt, len); len=0; } else len++; if (feof(fp)) break; } if (len) printf ("\n String %d, len=%d", ++cnt, len); Из-за особенностей реализации fgetc под Windows, без последней проверки за телом цикла код мог "не обратить внимания", например, на последнюю строку файла, состоящую только из пробелов и не завершающуюся переводом строки. Пример 5. Читаем построчно файл с известной максимальной длиной строки. Пусть файл уже открыт для чтения. char buf[128]; while (1) { fgets (buf,127, fp); if (feof(fp)) break; int len = strlen(buf); if (buf[len-1] =='\n') buf[len-1]='\0'; puts (buf); //Вывести прочитанные строки на экран } Без дополнительной обработки прочитанные из файла строки при выводе будут содержать "лишние" пустые строки между строками данных. Это происходит потому, что функция fgets читает строку файла вместе с символом перевода строки, а функция puts добавляет к выводимой строке ещё один перевод строки. Если максимальная длина строки принципиально не ограничена, помочь может либо предварительное посимвольное чтение файла для её определения, либо работа с файлом как с бинарными данными. while ((arr[i] = fgetc (file)) != EOF) // посимвольное чтение файла и запись в строку { if (arr[i] == '\n') { arr[i] = '\0'; printf ("%s\n», arr); i = 0; } else i++; } arr[i] = '\0'; printf("%s\n",arr); Приведенный в качестве примера код выводит данные из файла на экран. Запись в текстовый файл Также как и ввод, вывод (запись) в файл может быть различным: 1. Форматированный вывод. Функция fprintf (файловый_указатель, строка_формата, переменные). int fprintf (FILE * stream, const char *format [, argument, ...]); 2. Построчный вывод. Функция fputs (строка, файловый_указатель). Функция fputs( ) записывает строку символов в файл. Например: fputs ("Ехаmple", fp); При возникновении ошибки возвращается значение EOF. 3. Посимвольный вывод. Функция fputc() или putc(символ, файловый_указатель). int fputc (int c, FILE * stream); Ниже приводятся примеры кода, в которых используются три способа вывода данных в файл. 1. Запись в каждую строку файла полей одной структуры: file = fopen ("fprintf.txt", "w"); for (i = 0; i < n; i++) { scanf ("%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)); fprintf (file, "%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); } 2. Построчный вывод в файл (fputs(), в отличие от puts(), сама fputs() не помещает в конце строки '\n'): while (gets (arr) != NULL) { fputs (arr, file); fputs ("\n", file); } 3. Пример посимвольного вывода: while ((i = getchar ()) != EOF) putc (i, file); Пример 6. Программа реализует чтение строк из одного файла и ввод данных о товарах с клавиатуры в другой файл: #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #define N 80 int main () { setlocale (LC_ALL, "rus"); struct food { char name [20]; unsigned qty; float price; }; struct food shop [10]; char i = 0; FILE* file; char arr[N]; file = fopen ("fscanf.txt", "r"); while (fgets (arr, N, file) != NULL) printf ("%s", arr); printf("\n"); fclose(file); printf ("Запись в файл fprintf.txt - 3 товара\n"); file = fopen ("fprintf.txt", "w"); for (i = 0; i < 3; i++) { scanf ("%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)); fprintf (file, "%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); } printf("\n"); fclose(file); file = fopen ("fprintf.txt", "r"); printf ("Чтение из файла fprintf.txt - 3 товара\n"); while (fgets (arr, N, file) != NULL) printf ("%s", arr); printf("\n"); fclose(file); system("pause"); }
«Определение, открытие, закрытие, ввод-вывод файла. Произвольный доступ к файлу. Работа с текстовыми файлами. Библиотечные функции для работы с файлами» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot