Структуры и алгоритмы компьютерной обработки данных

Стандартные файлы и функции по работе с ними

Показывать лекцию целиком

Цель лекции: изучить понятия, характеристику и классификацию файлов и стандартных функций по работе с файлами, научиться решать задачи с использованием стандартных функций по работе с файлами на языке C++.

Файл – это именованная область внешней памяти, в которой хранится логически завершенный объем данных. Файл имеет следующие характерные особенности:

  • имеет имя на диске, что дает возможность программам идентифицировать и работать с несколькими файлами;
  • длина файла ограничивается только емкостью диска.
  • Часто бывает необходимо ввести некоторые данные из файла или вывести результаты в файл. Например, бывает необходимо обрабатывать массивы, которые слишком велики, чтобы полностью разместиться в памяти.

    Файлы делятся на текстовые и двоичные.

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

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

    Особенностью языка С++ является отсутствие в нем структурированных файлов. Все файлы рассматриваются как неструктурированная последовательность байтов. При таком подходе понятие файла распространяется и на различные устройства.

    В С++ существуют специальные средства ввода-вывода данных. Все операции ввода-вывода реализуются с помощью функций, которые находятся в библиотеке С++. Библиотека С++ поддерживает три уровня ввода-вывода:

  • потоковый ввод-вывод;
  • ввод-вывод нижнего уровня;
  • ввод-вывод для консоли и портов (зависит от ОС).
  • Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.

    Функции библиотеки ввода-вывода языка С++, поддерживающие обмен данными с файлами на уровне потока, позволяют обрабатывать данные различных размеров и форматов, обеспечивая при этом буферизованный ввод и вывод. Таким образом, поток представляет собой этот файл вместе с предоставленными средствами буферизации.

    Чтение данных из потока называется извлечением, вывод в поток – помещением (включением).

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

    (рис 19.1) Буферизация данных при работе с потоками

    При работе с потоком можно:

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

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

    В С++ операции с файлами можно осуществлять в двух режимах: форматированном и потоковом.

    Рассмотрим основные функции для работы с файлами в форматированном режиме.

    Функция открытия файла

    Для работы с файлом в языке C++ необходима ссылка на файл. Для определения такой ссылки существует структура FILE, описанная в файле stdio.h. Данная структура содержит все необходимые поля для управления файлами, например: текущий указатель буфера, текущий счетчик байтов, базовый адрес буфера ввода-вывода, номер файла.

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

    Например:

    #include<stdio.h>
    ..............
    FILE *fp;

    Для открытия файла существует функция fopen, которая инициализирует файл.

    Синтаксис:

    fp=fopen(ИмяФайла, РежимОткрытия);

    где fp – указатель на поток (файловый указатель);

    ИмяФайла – указатель на строку символов, представляющую собой допустимое имя файла, в которое может входить спецификация файла (включает обозначение логического устройства, путь к файлу и собственно имя файла);

    РежимОткрытия – указатель на строку режима открытия файла.

    Например:

    fp=fopen("t.txt","r");

    Существуют несколько режимов открытия файлов.

    Режимы открытия файлов
    Режим Описание Начинается с...
    r Открывает текстовый файл для чтения. Если файл не существует, то выдается ошибка при исполнении программы. начала
    w Открывает текстовый файл для записи. Если файл не существует, то он создается. Если файл уже существует, то удаляется его содержимое, файл перезаписывается начала
    a Открывает текстовый файл для добавления. Если файл не существует, то он создается. Если существует, то содержимое из него не удаляется. конца
    r+ Открывает текстовый файл для чтения и записи. Изменить размер файла нельзя. Если файл не существует, то выдается ошибка при исполнении программы. начала
    w+ Открывает текстовый файл для чтения и записи. Если файл не существует, то он создается. Если файл уже существует, то удаляется его содержимое, файл перезаписывается. начала
    a+ Открывает текстовый файл для чтения и записи. Если файл не существует, то он создается. Если существует, то содержимое из него не удаляется. конца
    rb Открывает двоичный файл для чтения. Если файл не существует, то выдается ошибка при исполнении программы. начала
    wb Открывает двоичный файл для записи. Если файл не существует, то он создается. Если файл уже существует, то удаляется его содержимое, файл перезаписывается. начала
    ab Открывает двоичный файл для добавления. Если файл не существует, то он создается. Если существует, то содержимое из него не удаляется. конца
    r+b Открывает двоичный файл для чтения и записи. Изменить размер файла нельзя. Если файл не существует, то выдается ошибка при исполнении программы. начала
    rb+
    w+b Открывает двоичный файл для чтения и записи. Если файл не существует, то он создается. Если файл уже существует, то удаляется его содержимое, файл перезаписывается. начала
    wb+
    a+b Открывает двоичный файл для чтения и записи. Если файл не существует, то он создается. Если существует, то содержимое из него не удаляется. конца
    ab+

    Поток можно открыть в текстовом ( t ) или двоичном ( b ) режиме. По умолчанию используется текстовый режим. В явном виде режим указывается следующим образом:

    "r+b" или "rb" – двоичный (бинарный) режим;

    "r+t" или "rt"текстовый режим.

    В файле stdio.h определена константа EOF, которая сообщает об окончании файла (отрицательное целое число).

    При открытии файла могут возникать следующие ошибки:

  • файл, связанный с потоком не найден (при чтении из файла);
  • диск заполнен (при записи);
  • диск защищен от записи (при записи) и т. п.
  • В этих случаях указатель на поток приобретет значение NULL (0). Указатель на поток, отличный от аварийного, не бывает равен NULL.

    Для вывода сообщения об ошибке при открытии файла используется стандартная библиотечная функция из файла <stdio.h>:

    void perror (const char*s);

    Функция perror() выводит строку символов, адресуемую указателем s, за которой размещаются: двоеточие, пробел и сообщение об ошибке. Содержимое и формат сообщения определяются реализацией системы программирования.

    Пример 1. Обнаружение и вывод ошибки при открытии файла.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      FILE *fp;
      if ((fp=fopen("t.txt", "r"))==NULL)
        // выводит строку символов с сообщением об ошибке
        perror("ошибка при открытии файла"); 
      system("pause");
      return 0;
    }

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

    Функция закрытия файла

    Открытые на диске файлы после окончания работы с ними рекомендуется закрыть явно. Это является хорошим тоном в программировании.

    Синтаксис:

    int fclose(УказательНаПоток);

    Возвращает 0 при успешном закрытии файла и -1 в противном случае.

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

    Функция удаления файла

    Синтаксис:

    int remove(const char *filename);

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

    Функция переименования файла

    Синтаксис:

    int rename(const char *oldfilename, const char *newfilename);

    Функция переименовывает файл; первый параметр – старое имя файла, второй – новое. Возвращает 0 при неудачном выполнении.

    Функция контроля конца файла

    Для контроля достижения конца файла есть функция feof.

    int feof(FILE * filename);

    Функция возвращает ненулевое значение, если достигнут конец файла.

    Функции ввода-вывода данных файла

    1) Символьный ввод-вывод

    Для символьного ввода-вывода используются функции:

    int fgetc(FILE *fp);

    где fp – указатель на поток, из которого выполняется считывание.

    Функция возвращает очередной символ в формате int из потока fp. Если символ не может быть прочитан, то возвращается значение EOF.

    int fputc(int c, FILE*fp);

    где fp – указатель на поток, в который выполняется запись;

    c – переменная типа int, в которой содержится записываемый в поток символ.

    Функция возвращает записанный в поток fp символ в формате int. Если символ не может быть записан, то возвращается значение EOF.

    Пример 2.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      FILE *f; 
      int c;
      char *filename="t.txt";
      if ((f=fopen(filename,"r"))==0)
        perror(filename);
      else 
        while((c = fgetc(f)) != EOF)
          putchar(c);
          //вывод с на стандартное устройство вывода
      fclose(f);
      system("pause");
      return 0;
    }

    2) Строковый ввод-вывод

    Для построчного ввода-вывода используются следующие функции:

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

    где char *s – адрес, по которому размещаются считанные байты;

    int n – количество считанных байтов;

    FILE *f – указатель на файл, из которого производится считывание.

    Прием байтов заканчивается после передачи n-1 байтов или при получении управляющего символа '\n'. Управляющий символ тоже передается в принимающую строку. Строка в любом случае заканчивается '\0'. При успешном завершении считывания функция возвращает указатель на прочитанную строку, при неуспешном – 0.

    int fputs(char *s, FILE *f);

    где char *s – адрес, из которого берутся записываемые в файл байты;

    FILE *f – указатель на файл, в который производится запись.

    Символ конца строки ( '\0' ) в файл не записывается. Функция возвращает EOF, если при записи в файл произошла ошибка, при успешной записи возвращает неотрицательное число.

    Пример 3. Построчное копирование данных из файла f1.txt в файл f2.txt.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    #define MAXLINE 255 //максимальная длина строки
    int _tmain(int argc, _TCHAR* argv[]){
    //копирование файла in в файл out
      FILE *in, //исходный файл
           *out; //принимающий файл
      char buf[MAXLINE]; 
      //строка, с помощью которой выполняется копирование
      in=fopen("f1.txt","r");
      //открыть исходный файл для чтения 
      out=fopen("f2.txt","w");
      //открыть принимающий файл для записи
      while(fgets(buf, MAXLINE, in)!=0) 
      //прочитать байты из файла in в строку buf
        fputs(buf, out);
        //записать байты из строки buf в файл out
      fclose(in); //закрыть исходный файл
      fclose(out);//закрыть принимающий файл
      system("pause");
      return 0;
    }

    3) Блоковый ввод-вывод

    Для блокового ввода-вывода используются функции:

    int fread(void *ptr, int size, int n, FILE *f);

    где void *ptr – указатель на область памяти, в которой размещаются считанные из файла данные;

    int size – размер одного считываемого элемента;

    int n – количество считываемых элементов;

    FILE *f – указатель на файл, из которого производится считывание.

    В случае успешного считывания функция возвращает количество считанных элементов, иначе – EOF.

    int fwrite(void *ptr ,int size, int n, FILE *f);

    где void *ptr – указатель на область памяти, в которой размещаются считанные из файла данные;

    int size – размер одного записываемого элемента;

    int n – количество записываемых элементов;

    FILE *f – указатель на файл, в который производится запись.

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

    Пример 4.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    struct Employee {char name[30];
                     char title[30];
                     float rate;
                    };
    int _tmain(int argc, _TCHAR* argv[]){
      Employee e;
      FILE *f;
      if((f=fopen("text.dat","w"))==NULL) {
        printf("\nФайл не открыт для записи");
      }
      int n;
      //запись в файл
      printf("\nВведите количество записей N=");
      scanf("%d",n);
      for(int i=0;i<n;i++) {
        //формируем структуру е 
        printf("имя:");scanf("%s",e.name);
        printf("наименование:");scanf("%s",e.title);
        printf("налог:");scanf("%f",e.rate);
        //записываем е в файл
        fwrite(e,sizeof(Employee),1,f);
      }
      fclose(f);
      //чтение из файла
      if((f=fopen("text.dat","rb"))==NULL) 
        printf("\nФайл не открыт для чтения");
      while(fread(e,sizeof(Employee),1,f)) 
        printf("%s, %s, %f;\n", e.name, e.title, e.rate);
      fclose(f);
      system("pause");
      return 0;
    }

    4) Форматированный ввод-вывод

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

    int fprintf(FILE *f, const char *fmt, ...);

    где FILE*f – указатель на файл, в который производится запись;

    const char *fmt – форматная строка;

    par1, par2, ... – список переменных, в которые заносится информация из файла.

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

    Пример 5.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      FILE *f;
      int n, nn,m;
      if((f=fopen("int.dat","w"))==0)
        perror("int.dat");
      for(n=1;n<11;n++)
        fprintf(f, "\n%d %d", n, n*n);
      fclose(f);
      if ((f=fopen("int.dat","r"))==0)
        perror("int.dat");
      m=1;
      while(fscanf(f, "%d %d",n, nn) m++<11) 
        printf("\n%d %d",n,nn);
      fclose(f);
      system("pause");
      return 0;
    }

    Основные алгоритмы работы с файлами

    Порядок работы с текстовыми и двоичными файлами аналогичен.

    Для записи данных в файл нужно выполнить:

  • Описать файловую переменную типа FILE *.
  • Открыть файл для записи с помощью функции fopen.
  • Записать данные в файл с помощью функции fprintf или fwrite.
  • Закрыть файл с помощью функции fclose.
  • Для чтения данных из файла нужно выполнить:

  • Описать файловую переменную типа FILE *.
  • Открыть файл для чтения с помощью функции fopen.
  • Считать данные из файла с помощью функции fscanf или fread.
  • Закрыть файл с помощью функции fclose.
  • Пример 6. Написать программу, реализующую подсчет количества символов в заданном тексте и файловый ввод-вывод данных. Работа программы должна включать ввод пользователем с клавиатуры имен входного и выходного файлов. Результат работы программы сохраняется в выходном файле, а также выводится на экран.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[]){
      int sum=0;
      char c,file1[10],file2[10];
      FILE *t,*g;
      printf("Введите имя входного файла : ");
      scanf("%s",file1);
      printf("Введите имя выходного файла  ");
      scanf("%s",file2);
      t=fopen(file1,"r");
      g=fopen(file2,"w");
      printf("\nСодержимое входного файла %s:\n",file1);
      while(!feof(t)) {
        c=getc(t);
        if (c!=10 c!=-1) 
        //символы конца строки и конца файла не считаются
        sum++;
        printf("%c", c);
      }
      fprintf(g,"%d",sum);
      printf("\nКоличество символов в тексте входного файла равно %d",sum);
      fclose(t);
      fclose(g);
      system("pause");
      return 0;
    }

    Ключевые термины

    Буфер – это область оперативной памяти, предназначенная для временного хранения данных во время процессов ввода-вывода информации.

    Двоичный файл – это файл, данные которого представлены в бинарном виде.

    Извлечение из потока – это процесс чтения данных из потока.

    Помещение (включение) в поток – это процесс вывода данных в поток.

    Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.

    Режимы открытия файлов – это наборы допустимых действий с открываемыми в программе файлами.

    Стандартные функции по работе с файлами – это функции из стандартной библиотеки, выполняющие ввод-вывод в файлы.

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

    Файл – это именованная область внешней памяти, в которой хранится логически завершенный объем данных.

    Файловый указатель – это указатель на объект структурного типа FILE.

    Краткие итоги

  • Данные хранятся на внешних носителях в виде файлов. Файлы рассматриваются как неструктурированная последовательность байтов.
  • Согласно концепции С++, файлы классифицируются на текстовые и двоичные.
  • Организация ввода-вывода в С++ осуществляется с помощью потоков.
  • Чтение данных из потока называется извлечением, вывод в поток – помещением (включением).
  • Ввод-вывод данных в файлах с помощью стандартных функций осуществляется посредством буфера.
  • При организации в программе файлового потока объявляется файловый указатель.
  • Прототипы функций по работе с файлами находятся в стандартных библиотеках.
  • Перед началом работы с файлом его необходимо открыть, указав режим открытия.
  • Любой открытый в программе файл необходимо закрыть после использования.
  • С помощью стандартных функций может быть организован символьный, строковый, блоковый и форматированный ввод-вывод данных.
  • Лабораторная работа 19. Стандартные файлы и функции по работе с ними

    Цель работы: изучить понятия, характеристику и классификацию файлов и стандартных функций по работе с файлами, научиться решать задачи с использованием стандартных функций по работе с файлами на языке C++.

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

    Теоретические сведения.

    Ознакомьтесь с материалом лекции 19.

    Задания к лабораторной работе.

    Выполните приведенные ниже задания.

  • Определить, какая буква чаще всего встречается в заданном файле.
  • Удвоить в содержимом файла каждую литеру.
  • Подсчитать число цифр в данном файле и их сумму.
  • Определить переменную "студент", содержащую следующие поля: имя, фамилия, отчество студента, название института, номер группы. Данные считать из текстового файла. Вывести данные о студентах, которые учатся в одном и том же заданном институте и в одной заданной группе.
  • Указания к выполнению работы.

    Каждое задание необходимо решить в соответствии с изученными методами обработки стандартных файлов в языке С++. В программном коде к каждой задаче следует выполнять чтение данных из существующего входного файла (имя файла вводится в виде строки символов и считается корректным с точки зрения организации файловой системы). В задаче 2 возможно использование вспомогательного файла, который выступает в роли выходного файла. Программу для решения каждого задания необходимо разработать методом процедурной абстракции, оформив комментарии к коду.

    Следует реализовать каждое задание в соответствии с приведенными этапами:

  • изучить словесную постановку задачи, выделив при этом все виды данных;
  • сформулировать математическую постановку задачи;
  • выбрать метод решения задачи, если это необходимо;
  • разработать графическую схему алгоритма;
  • записать разработанный алгоритм на языке С++;
  • разработать контрольный тест к программе;
  • отладить программу;
  • представить отчет по работе.
  • Требования к отчету.

    Отчет по лабораторной работе должен соответствовать следующей структуре.

  • Титульный лист.
  • Словесная постановка задачи. В этом подразделе проводится полное описание задачи. Описывается суть задачи, анализ входящих в нее физических величин, область их допустимых значений, единицы их измерения, возможные ограничения, анализ условий при которых задача имеет решение (не имеет решения), анализ ожидаемых результатов.
  • Математическая модель. В этом подразделе вводятся математические описания физических величин и математическое описание их взаимодействий. Цель подраздела – представить решаемую задачу в математической формулировке.
  • Алгоритм решения задачи. В подразделе описывается разработка структуры алгоритма, обосновывается абстракция данных, задача разбивается на подзадачи. Схема алгоритма выполняется по ЕСПД (ГОСТ 19.003-80 и ГОСТ 19.002-80).
  • Листинг программы. Подраздел должен содержать текст программы на языке программирования С++, реализованный в среде MS Visual Studio 2010.
  • Контрольный тест. Подраздел содержит наборы исходных данных и полученные в ходе выполнения программы результаты.
  • Выводы по лабораторной работе.
  • Ответы на контрольные вопросы.
  • Контрольные вопросы

  • В чем отличия текстовых и двоичных файлов с точки зрения представления данных?
  • Почему поток ввода-вывода не зависит от конкретного устройства?
  • Для чего необходима буферизация при потоковом и форматированном вводе-выводе?
  • С какой целью предусмотрены режимы открытия файлов и почему их такое многообразие?
  • Каковы могут быть причины ошибок при открытии файлов?
  • Какие значения возвращает функция открытия файла в зависимости от результата?
  • Каким образом в программе происходит проверка достижения конца файла?
  • Может ли один и тот же указатель на файл одновременно связан с несколькими файлами? Почему?
  • Может ли один и тот же файл одновременно быть открыт для чтения и для записи?
  • Можно ли один и тот же файл открыть несколько раз, не закрывая после каждого открытия?
  • Сохранится ли информация в файле, если его не закрыть в программе с помощью функции?
  • В чем основные отличия в организации символьного, строкового, блокового и форматированного ввода-ввода в файлы?
  • Вернуться к учебному плану