Лекция_2._Обработка_симв_инф_Файлы_Программирование_2_семестр (1271738), страница 2
Текст из файла (страница 2)
/*или можно описать тот же самый шаблон так:
struct BOOK {char author[20]; char title[44];
int year; float price} ;*/
BOOK b;/*описание структурной переменной b*/
Память, занимаемая структурой, равна сумме объемов памяти полей (если исключить из рассмотрения особенности, связанные с выравниванием). В любом случае для определения размера памяти структуры можно использовать операцию sizeof(). Шаблон ВООК, например, описывает структуру размером памяти 70.
Обращение к полю структурной переменной:
ИмяСтруктуры.ИмяПоля или АдресСтруктуры->ИмяПоля
.(точка) и -> являются операциями, соответственно, прямого и косвенного выбора компоненты структурированной переменной.
Например,
struct BOOK a,*pnta=&a;...
a.author="Byron"; pnta->author="Byron"; /*эквивалентные операторы*/
Примеры программ с использованием структур рассмотрим после того, как нучимся работать с файлами, так как такие программы обычно связаны с обработкой больших объемов данных.
В современном программировании структуры, прежде всего, нужны для объединения компонентов разного типа в одну переменную в соответствии с логикой задачи; тем самым, примение структур улучшает восприятие программы.
В Си существует еще один сложный тип, описание которого формально похоже на структуру. Это тип (и переменная) объединение.
Объединение - это переменная, содержащая поля разного типа, помещаемые в одно и то же место памяти. По существу объединение дает способ различной интерпретация содержимого памяти. Описание шаблона (типа) объединения и переменной этого типа выполняется также, как для структуры, только вместо ключевого слова struct используется union. Размер памяти, занимаемой объединением, равен максимальному из размеров полей.
2.3.Файлы в Си
2.3.1. Общее понятие о файлах в программировании
Термин файл используется в программировании в двух смыслах. В рамках операционной системы файл понимается как область памяти внешнего запоминающего устройства (обычно диска), имеющая имя. В алгоритмических языках файл - это тип данных, используемый, как правило, при работе с внешними запоминающими устройствами. В Си нет специального типа файл, но есть предопределенный структурный тип FILE, описание которого содержится в заголовочном файле stdio.h.
Файл - это последовательность однотипных компонент. Число компонент файла не ограничено. Компонентой файла может быть строка или байт, или значение какого-либо типа (целого, вещественного, структура, и т. д.). После последней компоненты файла стоит специальный код, называемый признаком конца файла; этот код обычно ставится автоматически, без участия программиста.
В классическом понимании файл рассматривается как абстрактное обобщение данных на устройстве с последовательным доступом, типичным представителем которого является магнитная лента. В библиотеках современных языков программирования, конечно, имеются средства и для работы с файлами прямого доступа.
Абстрактная последовательность данных на некотором внешнем устройстве также называется потоком.
Обычно в алгоритмических языках определяется программное и физическое имя файлов. Программное имя - это имя переменной типа файл (в СИ типа FILE*), физическое имя - это имя файла на внешнем запоминающем устройстве. Существуют специальные операторы, связывающие программное и физическое имя файла (см.§2.3.2).
Файлы бывают текстовыми и двоичными.
Текстовые файлы хранят информацию во внешнем представлении. Они имеют два основных признака: во-первых, их компонентами являются строки символов; во-вторых, эта символьная информация при чтении из файла или записи в файл преобразуется в соответствии с типом вводимых или выводимых переменных. Эти файлы являются обобщением данных на устройстве консоль (CON). Умение работать с текстовыми файлами необходимо даже начинающим программистам, так как обычно имеется потребность в длительном хранении исходных и выходных данных.
Текстовые файлы можно ввести в ЭВМ, прочитать, исправить с помощью текстового редактора. Они хранят информацию во внешнем представлении, в виде, понятном для человека. Такие файлы часто называются видимыми.
Двоичный файл - это последовательность байтов; обмен информации между двоичным файлом и данными программы происходит без преобразования.
2.3.2. Функции Си для работы с файлами
Шаблон структуры FILE и прототипы основных функций работы с файлами хранятся в stdio.h. Перед использованием файла необходимо:
1. Описать его программное имя как указатель на структуру типа FILE:
FILE *ИмяУказателя
-
Открыть файл. Функция открытия файла устанавливает связь между программным и физическим именами файла и подготавливает его к использованию.
Шаблон функции открытия файла:
FILE *fopen(char *ИмяФайла, char *режим);
первый параметр задает строку, в которой хранится физическое имя файла, записанное по правилам операционной системы, второй - режим открытия.
Приведем некоторые режимы:
“r” - открыть для чтения;
“w” - открыть для записи;
“rb” - открыть двоичный файл для чтения;
“wb” - открыть двоичный файл для записи;
“rt” - открыть текстовый файл для чтения;
“wt” - открыть текстовый файл для записи;
“a” – открыть файл для дополнения.
Результат функции fopen необходимо присвоить переменной типа FILE *. Если функции fopen удалось открыть указанный файл, возвращается указатель на FILE. Если же файл не может быть открыт, возвращается NULL.
Пример открытия файла и обнаружения ошибки при открытии:
FILE *fp;
fp=fopen(“x.txt”,”w”);
if (fp==NULL)
puts(“Ошибка при открытии файла”)
else ....
Или:
FILE *fp;
if ((fp=fopen(“x.txt”,”w”))==NULL)
{ puts(“Ошибка при открытии файла”);
exit(1); }.
Функция с шаблоном
int feof(FILE *fp);
возвращает значение, не равное 0 (истина), если (при открытии файла, или чтении из файла, или записи в файл) конец файла достигнут, и значение 0 (ложь) в противном случае.
В стандартной библиотеке Си функции ввода-вывода, в частности доступа к файлам, могут возвращать значение, равное символьной константе (точнее макроопределению) EOF для индикации, что достигнут конец файла. Реальное значение EOF является отрицательным числом, зависящим от системы (в основном −1), что гарантирует несовпадение с кодом символа.
Далее поясним, что такое открытие файла для чтения, записи и дополнения.
Открытие файла для чтения предусматривает выполнение следующих действий:
-
Поиск файла с заданным физическим именем; если файл не найден, то выводится сообщение об ошибке.
-
Указатель файла устанавливается на первую компоненту.
-
Компонента, на которой стоит указатель, считывается в буфер.
-
Если считан признак конца файла, то функция feof устанавливается в значение истина, иначе – в значение ложь.
Открытие файла для записи предусматривает выполнение следующих действий:
-
Поиск файла с заданным физическим именем; если файл не найден, то создается новый файл.
-
Содержимое файла очищается.
-
Указатель файла устанавливается на конец файла, который, поскольку файл очищен, одновременно является его началом.
-
Функция feof устанавливается в значение истина. Это значение сохраняется при последующей записи в файл, т. е. запись происходит в конец файла.
Открытие файла для дополненния предусматривает выполнение следующих действий:
-
Поиск файла с заданным физическим именем; если файл не найден, то выдается сообщение об ошибке.
-
Указатель файла устанавливается на конец файла (после последней компоненты).
-
Функция feof устанавливается в значение истина.
Рассмотренные ранее функции scanf, printf, puts, gets имеют аналоги для работы с текстовыми файлами. Их названия получаются добавлением справа буквы f к именам упомянутых функций.
Шаблоны функций файлового форматного вывода и ввода:
int fprintf(програм_имя_ файла, форматная_строка, список_вывода);
int fscanf(програм_имя_ файла, форматная_строка, список_вывода);
Шаблоны функций fputs и fgets:
char *fgets(char *string, int nmax,FILE *f)
char *fputs(char *string, FILE *f)
где string - адрес начала вводимого массива символов,
f - указатель на текстовый файл,
nmax - максимальная длина вводимой строки плюс 1.
Функции putc (записать символ в поток) и getc (прочитать символ из потока) работают как с текстовыми, так и с двоичными файлами. Их шаблоны:
int putc(int ch, FILE* f) (если оператор выполнен успешно, то возвращается записанный символ, иначе EOF)
int getc(FILE *f) (если считывание не выполнено, то EOF)
Для работы с двоичными файлами предназначены функции чтения и записи блоков:
unsigned fread(void *buf, int zap, int n,FILE *f)
unsigned fwrite(const void *buf, int zap, int n,FILE *f)
где buf - указатель на область памяти, с которой будет происходить обмен информацией, zap – размер в байтах считываемого или записываемого элемента (записи), n – максимальное число считываемых (записываемых) элементов, f - указатель на файл.
Функция fread (fwrite) возвращает количество прочитанных (записанных) записей, которое стандартно равно n, но может быть меньше n при возникновении ошибки или достижении конца файла до считывания n записей.
После окончания работы с файлом его необходимо закрыть. При закрытии файла выполняется его сохранение (естественно, с перерегистрацией в каталоге). Кроме того, связь между программным и физическим именем файла, установленная при открытии файла, разрушается.
Шаблон функции закрытия файла:
int fclose(FILE * f) - возвращает значение нуль, если операция закрытия прошла успешно.
2.3.3. Примеры программ, использующих файлы
Пример 1. Размеры матрицы n и m и значения элементов матрицы считываются из текстового файла f_in.txt , затем матрица изменяется (скажите, как) и выводится в текстовый файл f_out.txt.
#include <stdio.h>
#include <conio.h>
void main()
{
float a[5][5]; int n,m,i,j;
FILE* f_in, *f_out;
f_in=fopen("f_in.txt","rt"); //"c:\\dir\\f_in.txt"
fscanf(f_in,"%d%d", &n,&m);
for (i=0;i<n;i++)
for (j=0;j<m;j++)
fscanf(f_in,"%f",&a[i][j]);//fscanf(f_in,"%f", *(a+i)+j);
//закончен ввод из файла
for (i=0;i<n;i++)
for (j=0;j<m;j++)
a[i][j]=a[i][j]*a[i][j];
//закончена обработка матрицы
f_out=fopen("f_out.txt","wt");
fputs("output matrix:\n",f_out);
for (i=0;i<n;i++)
{
for (j=0;j<m;j++)
fprintf(f_out,"%6.1f ",a[i][j]);
fprintf(f_out,"\n");
}
// вывели изменную матрицу в файл
fclose(f_in);//необязательно
fclose(f_out);//обязательно!!!!!
puts("output file is ready!"); puts("output file is ready!");
//каждая фраза на отдельной строке
_getch();
}
Пример 2. В каждой строке входного файла f_in.txt через пробел записаны фамилия и имя человека. Каждая строка входного файла считывается в переменную-строку s. В выходной файл f_out.txt записываются строки входного файла, содержащие фамилии «Иванов» и «Иванова». Обратите наличие пробела после фамилий в соответствующих строковых литералах программы. Зачем он нужен?
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define NMAX 100
void main()
{
char s[NMAX]; int k,i;//k-число Ивановых