Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

учебное пособие. Часть1. Информатика

.pdf
Скачиваний:
43
Добавлен:
04.06.2015
Размер:
2.87 Mб
Скачать

Текстовый файл можно представить как набор строк произвольной длины. Для деления на строки используется управляющий символ ’\n’. Он является файлом последовательного доступа. Логически текстовый файл можно представить как именованную цепочку байтов, имеющую начало и конец. Чтение (или запись) из файла (в файл) ведется байт за байтом от нача- ла к концу. Перед первым обращением к функциям ввода-вывода указатель

файла стоит в его начале и указывает на первый компонент. После каждого чтения или записи указатель сдвигается на заданное количество байт. Такой способ доступа называется последовательным. Текстовые файлы оказывают- ся переносимыми с одного типа компьютера на другой, если символы, со- держащиеся в файле, принадлежат стандартному набору символов (т. е. не используются дополнительные символы, такие, например, как символы псевдо- графики фирмы IBM) [6].

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

Рассмотрим текстовые файлы. Каждой программе доступны два стан-

дартных файла stdin (клавиатура) и stdout (экран). Это текстовые фай- лы. Любые другие файлы становятся доступными после выполнения специ- альных процедур. Рассмотрим основные функции для работы с текстовыми файлами.

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

FILE *fopen (char *filename, char *type)

Эта функция возвращает указатель на файл, который открывается. Ес- ли файл открыть нельзя, то функция возвращает пустой указатель NULL. char *filename указатель на строку символов, содержащую имя файла

и, если необходимо, путь к нему. char *type указатель на строку симво- лов, определяющую тип открываемого файла. Допустимы следующие значе- ния этой переменной [6]:

”r” открыть уже существующий файл на ввод;

”w” cоздать новый файл или очистить уже существующий файл и открыть его на вывод;

”a” cоздать новый файл для вывода или осуществить вывод в конец уже существующего файла;

”r+”открыть уже существующий файл для обновления, которое бу- дет проводиться с начала файла;

”w+”− cоздать новый файл или очистить уже существующий файл для обновления его содержимого;

251

”a+”− cоздать новый файл или подстроиться в конец существующего для обновления его содержимого.

Дополнительно к каждой из приведенных строк можно добавить сим- вол b, указывающий на то, что открывается двоичный файл. Если добавлен символ t или символ отсутствует, то открывается текстовый файл.

Примеры открытия файла:

FILE * in, *out;

in=fopen (”input.txt”, ”r”);

out=fopen (”c:\\users\\output.txt”, ”w”);

При заполнении файла после последней записи автоматически помеща- ется невидимый специальный признак "конец файла" (end of file). Существует функция feof(f), тестирующая конец файла. Функция feof(f) возвращает значение истина, если действительно встретился при- знак "конец файла"; пока это не произойдет, значение feof(f) будет ложь.

fclose(FILE * stream) функция закрытия файла, на который

указывает указатель на файл stream. Функция fclose() выполняется автоматически по отношению ко всем открытым файлам при нормальном за- вершении программы.

Для чтения и записи информации из файла или в файл используется очень много функций. Например, функции fscanf и fprintf, анало-

гичные уже знакомым функциям scanf и printf, но в качестве первого параметра в них выступает указатель на файл. Также применяется функция fputs, аналогичная puts, но имеющая второй параметр указатель на файл, в который намерены писать строку символов, и функция fgets, ана- логичная gets, но имеющая следующий синтаксис:

char *fgets(char *s, int n, FILE * stream)

Эта функция считывает из файла с указателем stream в строку s символы до тех пор, пока не будет выполнено одно из условий:

начнется новая строка;

достигнут конец файла;

прочитано n-1 символов.

fgets (s, 20, in);// Читается не более 20 символов

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

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

252

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

//Пример pr37

#include <cstdio> using namespace std; int main()

{

FILE *in;

int k = 0; //Количество положительных компонент float w; //Буферная переменная

if (( in = fopen ( "input.txt", "r" )) != NULL)

{

while ( !feof ( in ))

{

fscanf(in,"%f",&w);// Чтение из файла if ( w>0 ) k++;

}

printf(" k= %d\n", k);

}

else puts("Нет такого файла"); return 0;

}

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

дируются путем замены кода символа следующим кодом из таблицы ASCII и записываются во второй файл [17].

//Пример pr38 #include <string> #include <cstdio> using namespace std;

int main()

{

FILE *oldf, *newf;//Указатели стаpого и нового файла char oldn[10], newn[10],//Имена файлов

line[255]; //Буферная переменная для стpоки int c;

printf ( "Введите имя кодиpуемого файла:" ); scanf ( "%s", oldn );

printf ( "Введите имя нового файла:" ); scanf ( "%s", newn );

if (( oldf = fopen (oldn, "r" )) != NULL )

{

253

newf = fopen ( newn, "w" );

while ( fgets ( line, 255, oldf ) != NULL )

{

for ( c = 0; c<strlen (line) ;c++ ) if ( line[c] == 255) line[c] = 0; else line[c] = line[c] + 1;

fputs ( line, newf );

}

fclose ( newf );

}

else puts ( "Нет такого файла" ); return 0;

}

Следующий пример демонстрирует работу с текстовыми файлами, со- держащими данные типа struct.

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

ний балл каждого студента и вывести фамилию студента с максимальным средним баллом. Когда в файле хранятся данные типа struct, следует учи- тывать расположение данных. В приведенном ниже примере в каждой строке хранится фамилия и три оценки одного студента; под фамилию отводится не более 15 позиций (если фамилия короче, то дополняется пробелами), а оцен- ки отделяются друг от друга пробелами. Текст программы предлагается в примере pr39.

//Пример pr39 struct w

{ char fam[15]; int oc[3]; float sr; } ;

int main ()

{

w wed; // Пеpеменная для хранения данных о студенте

FILE *in; int j;

float max = 0, s; char maxfam[15];

//Открытие файла с исходными данными для чтения if (( in = fopen ( "isx.dan", "r" )) != NULL )

{

while ( fgets ( wed.fam, 15, in ) != NULL )

{

254

s = 0;

for ( j = 0; j<3; j++)

{ fscanf ( in, "%d", &wed.oc[j] ); s+ = wed.oc[j];

}

wed.sr = s/3.0;

if ( max < wed.sr ) { max = wed.sr;

strcpy ( maxfam, wed.fam ) ;

}

}

printf (" Максимальный средний балл у %s\n", maxfam);

}

else puts ( "Нет файла" ); return 0;

}

Рассмотрим пример, иллюстрирующий работу одновременно с не- сколькими файлами: среди заданного количества текстовых файлов найти файл, содержащий самую длинную строку. Имена текстовых файлов будем вводить с клавиатуры до тех пор, пока не встретим признак конец строки”. Программа представлена в примере pr40.

//Пример pr40 int main (void)

{

FILE *myfile; // Указатель на текущий файл int i = 0, max = 0;

char buff[200]; clrscr ();

char filename[15], //Имя текущего файла

maxfile[15]; // Имя файла с максимальной строкой printf ( "%s", "Введите имя файла: ");

scanf ("%s", filename); if ( !feof ( stdin ))

{

do

{myfile=fopen(filename,"rt"); while ( !feof ( myfile ))

{fgets( buff, 200, myfile);

i= strlen ( buff);

if ( max<i ) { max = i;

strcpy ( maxfile, filename );

}

255

};

fclose ( myfile ); //Закрываем текущий файл printf ( "\n%s", "Введите имя файла: " ); scanf ( "%s", filename );

}

while ( !feof ( stdin ));

}

printf (" Файл с максимальной длиной строки: %s ", maxfile); return 0;

}

9.2. Двоичные файлы

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

Открытие двоичного файла производится той же функцией, как в тек- стовом файле, но тип открываемого файла явно указывается:

in=fopen(“input.dat”,"rb");

Для организации прямого доступа к компонентам двоичного файла служит встроенная функция fseek:

fseek ( in, offset, wherefrom )

Здесь in – указатель файла прямого доступа; offset – выражение типа long, указывающее число байт смещения от точки, определяемой третьим параметром функции; wherefrom – указывает точку, от которой следует начинать отсчет смещения, заданного вторым аргументом. Для этого определены следующие константы:

#define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2

Значение 0 – смещение от начала файла. Значение 1 – смещение от текущей позиции файла. Значение 2 – смещение от конца файла.

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

fseek (in, 0L, SEEK_END);

Функция fseek возвращает значение 0, если указатель успешно пере- мещен, и возвращает ненулевое значение при ошибке.

Функция ftell возвращает текущее положение указателя файла. Смеще- ние измеряется в байтах, считая от начала файла.

long ftell (FILE *stream);

Функция rewind перемещает указатель файла на начало:

256

void rewind (FILE *in);

Аналогичное действие может быть выполнено и с помощью вызова fseek (in, 0L, SEEK_SET);

Для чтения и записи в двоичный файл используются функции fread и fwrite соответственно.

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

Функция fread читает n элементов, имеющих длину size байт, из вход- ного файла in в блок, указанный в ptr. Общее число прочитанных байтов рав- но (n*size); ptr является указателем на любой объект; size – размер объекта, на который указывает ptr. При успешном окончании функция возвращает число элементов (не байтов), в действительности прочитанных. Если встре- тится конец файла или ошибка, то функция fread возможно возвращает 0.

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

Функция fwrite добавляет n элементов данных, имеющих длину size байт, к файлу вывода out. Добавляемые данные начинаются с ptr; общее чис- ло записанных байтов равно (n * size); ptr является указателем на любой объ- ект; size – размер объекта, на который указывает ptr. При успешном оконча- нии функция возвращает число элементов (не байтов), в действительности записанных.

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

//Пример pr41 Пpогpамма создания двоичного файла // c помощью датчика случайных чисел

#include <cstdlib> #include <cstdio> using namespace std; int main()

{

FILE *in;

float a;

// Буфеpная пеpеменная

int n;

//Количество компонент, записываемых в файл

in = fopen ( "fileb.dat", "w+b" );

printf ( "\n Введите числo компонент файла: " ); scanf ( "%d", &n );

randomize();

for ( int i = 1; i<=n; i++ ) //Цикл записи { a = random (50)-25;

fwrite ( &a, sizeof(a), 1, in ); //Запись числа

}

rewind(in); //Установка указателя файла в начало fread ( &a, sizeof(a), 1, in );//Чтение числа из файла while ( !feof ( in )) //Пока не конец файла

257

{

printf ( "%f\t", a ); //Вывод на экpан fread ( &a, sizeof(a), 1, in );

}

fclose ( in ); return 0;

}

Приведенный пример сортировки является универсальным, так как при изменении структуры данных, хранящихся в файле, изменяется только опи- сание этой структуры, а весь алгоритм сортировки полностью сохраняется.

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

//Пример pr42 Пpогpамма создания двоичного файла //путем чтения данных из текстового файла

int main ()

{

FILE *in, //Файловая пеpеменная текстового файла *out; //Файловая пеpеменная двоичного файла

struct w1

{

char surname[10];//Фамилия float age;//Возpаст

char city[10];//Гоpод } w2;

out = fopen ( "fileb.dat", "w+b" );

if (( in = fopen ("filet.dat", "r" )) != NULL )

{

while ( fgets ( w2.surname, 10, in) != NULL )

{

fgets ( w2.city, 10, in );

fscanf ( in, "%f\n", &w2.age );

//Запись в двоичный файл данных на одного студента fwrite ( &w2, sizeof(w2), 1, out );

}

}

//Вывод на экpан из двоичного файла

puts ( "\nВ файле следующая инфоpмация" ); rewind ( out );

fread ( &w2, sizeof(w2), 1, out ); while ( !feof ( out ))

{

printf ( "%f\t %s\t%s \t\n", w2.age, w2.surname, w2.city ); fread ( &w2, sizeof(w2), 1, out );

258

}

fclose (in); fclose (out); return 0;

}

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

Контрольные вопросы

1.Что такое файл?

2.Чем различаются файлы последовательного и прямого доступа?

3.Как связать указатель на файл с именем файла?

4.Как создать двоичный файл?

5.Как создать текстовый файл?

6.Какие операции можно выполнять над файлами?

259

10.ДОПОЛНИТЕЛЬНЫЕ СВЕДЕНИЯ О ФУНКЦИЯХ

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

ции и т. д.

10.1.Область действия имен

Скаждой переменной языка С++ связаны два основополагающих свойства тип и класс переменной [5]. Типы переменных обсуждались в предыдущей части пособия. Класс памяти, если он не задается явно, назначается по умолчанию.

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

В языке С++ различают четыре класса памяти:

автоматический (auto)

внешний (extern)

статический (static)

регистровый (registr)

Таким образом, каждой переменной наряду с типом можно указать и класс памяти. Если класс памяти не указан, то он определяется по умолча- нию в зависимости от расположения описания переменной.

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

мере pr43 в главной функции описана автоматическая переменная i. Об- ласть ее действия ограничена главной функцией.

//Пример pr43 #include <сstdio >

260