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



Лабораторная работа №5

Функции и файлы

Цель работы: научиться разрабатывать пользовательские функции и работать с файлами на языке Си.

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

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

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

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

Описание функций

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

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

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

тип имя ([ список_формальных_параметров ]);

 

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

тип имя ([ список_формальных_параметров ])

{

тело функции

}

 

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

 

Рассмотрим составные части определения.

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

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

3. Функция активируется с помощью оператора вызова функции, в котором содержатся имя функции и параметры (если это необходимо). Вызов функции приводит к выполнению операторов, составляющих тело функции, и выглядит следующим образом:

имя ([ список_фактических_параметров ]).

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

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

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

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

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

 

Пример функции, возвращающей сумму двух целых величин:

#include "stdafx.h"

#include <iostream>

 

using namespace std;

 

int sum(int а, int b); // объявление функции

 

void main()

{

int a = 2, b = 3, c, d;

c = sum(a, b);        // вызов функции

cin >> d;

cout << sum(c, d) << endl; // вызов функции

  

a = 3*sum(c, d) - b;  // вызов функции

cout << a << endl;

return;

}

int sum(int a, int b) // определение функции

{

return (a + b);

}

 

Объявление и определение функции может совпадать, но перед вызовом компилятор должен знать прототип функции, поэтому одновременное объявление и определение должно следовать перед вызовом функции.

Пример функции нахождения максимума двух чисел:

#include "stdafx.h"

#include <iostream>

using namespace std;

int max(int x, int y) // одновременно объявление и определение функции

{

if (x > y)

  return x;

else

  return y;

}

void main()

{

int a = 2, b = 3, c, d;

c = max(a, b);         // вызов функции

cout << c << endl;

cin >> d;

cout << max(c, d) << endl; // вызов функции

return;

}

 

Параметры функции

Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями.

Параметры, перечисленные в заголовке описания функции, называются формальными параметрами, или просто параметрами, а записанные в операторе вызова функции – фактическими параметрами, или аргументами.

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

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

Существует два основных способа передачи параметров в функцию: по значению и по адресу.

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

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

 

#include "stdafx.h"

#include <iostream>

using namespace std;

void f(int a, int* b, int& c)

{

a++;

(*b)++;

c++;

}

 

void main()

{

  

int a = 1, b = 1, c = 1;

 cout << "a b c" << endl;

cout << a << ' ' << b << ' ' << c << endl;

 

f(a, &b, c);

 

cout << a << ' ' << b << ' ' << c << endl;

return;

}

 

Результат работы программы:

 

a b c

1 1 1

1 2 2

 

Первый параметр (a) передается по значению. Его изменение в функции не влияет на исходное значение. Второй параметр (b) передается по адресу с помощью указателя, при этом для передачи в функцию адреса фактического параметра используется операция взятия адреса, а для получения его значения в функции требуется операция разыменования. Третий параметр (c) передается по адресу с помощью ссылки.

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

Если требуется изменить значение параметра внутри функции, то он передается, либо через ссылку, либо через указатель.

Если требуется запретить изменение параметра внутри функции, используется модификатор const:

int f(const char *);

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

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

Если в определении функции присутствует параметр со значением по умолчанию, то данный параметр можно опустить при вызове функции. При этом все параметры левее данного должны присутствовать. Значение данного параметра внутри функции будет совпадать со значением параметра по умолчанию.

 

Передача массивов в функцию

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

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

 

#include "stdafx.h"

#include <iostream>

 

using namespace std;

 

//функция формирует вектор(одномерный массив),

//состоящий из максимальных элементов исходных массивов

//массивы передаются через указатели

void max_vect(int n, int *x, int *y, int *z)

{

for (int i=0; i<n; i++)

  z[i] = (x[i]>y[i])?x[i]:y[i];

}

 

void main()

{

setlocale(LC_ALL, "Russian");

  

//определим размерность массива

const int N=7;

  

int a[N]={1,4,3,-1,5,6,1};

int b[N]={7,6,-2,4,3,2,4};

int c[N];

  

//получим вектор, состоящий из максимальных элементов

//исходных массивов

max_vect(N, a, b, c);

  

//выведем на экран элементы массива

for (int i=0; i < N; i++)

  cout << "\t" << c[i];

}

 

Результат:

 

7 6 3 4 5 6 4

 

 

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

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

 

#include "stdafx.h"

#include <iostream>

 

using namespace std;

 

int sum(int *x, const int n, const int m)

{

int s = 0;

 for (int i=0; i < n; i++)

  for (int j=0; j < m; j++)

      //здесь производится пересчет индекса массива:

      //перемещение на одну строчку вниз

      //эквивалентно прибавлению длинны строки

      //поэтому x[i*m + j] и x[i][j] будут определять

      //один и тот же элемент

      s += x[i*m + j];

return s;

}

 

void main()

{

setlocale(LC_ALL, "Russian");

  

int a[2][2]={{1,2},{3,4}};

//имя массива a напрямую передавать нельзя из-за несоответствия типов

//поэтому используется конструкция &a[0][0]

cout << sum(&a[0][0], 2, 2);

}

 

 

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

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

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

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

2. длина файла ограничивается только емкостью диска.

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

Библиотека С поддерживает три уровня ввода-вывода:

- потоковый ввод-вывод;

- ввод-вывод нижнего уровня;

- ввод-вывод для консоли портов (зависит от конкретной ОС).

Потоковый ввод-вывод

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

1. Открывать и закрывать потоки (при этом указатели на поток связываются с конкретными файлами);

2. Вводить и выводить строки, символы, форматированные данные, порции данных произвольной длины;

3. Управлять буферизацией потока и размером буфера;

4. Получать и устанавливать указатель текущей позиции в файле.

Прототипы функций ввода-вывода находятся в заголовочном файле <stdio.h>, который также содержит определения констант, типов и структур, необходимых для обмена с потоком.

 

Открытие и закрытие потока

Прежде, чем начать работать с потоком, его надо инициировать, т. е. открыть. При этом поток связывается со структурой предопределенного типа FILE, определение которой находится в файле <stdio.h>. В структуре находится указатель на буфер, указатель на текущую позицию и т. п. При открытии потока возвращается указатель на поток, т. е. на объект типа FILE. Указатель на поток должен быть объявлен следующим образом:

#include <stdio.h>

. . . . . . . .

FILE *f;//указатель на поток

Указатель на поток приобретает значение в результате выполнения функции открытия потока:

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

где const char*filename – строка, которая содержит имя файла, связанного с потоком,

const char*mode – строка режимов открытия файла.

Например:

f=fopen(“t.txt”,”r”);

где t.txt – имя файла, r – режим открытия файла.

Файл связанный с потоком можно открыть в одном из 6 режимов

 

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

Поток можно открывать в текстовом (t) или двоичном режиме(b). В текстовом режиме поток рассматривается как совокупность строк, в конце каждой строки находится управляющий символ ‘\n’. В двоичном режиме поток рассматривается как набор двоичной информации. Текстовый режим устанавливается по умолчанию.

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

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

- файл, связанный с потоком не найден (при чтении из файла);

- диск заполнен (при записи);

- диск защищен от записи (при записи) и т. п.

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

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

void perror (const char*s);

Эта функция выводит строку символов, не которую указывает указатель s, за этой строкой размещается двоеточие пробел и сообщение об ошибке. Текст сообщения выбирается на основании номера ошибки. Номер ошибки заносится в переменную int errno(определена в заголовочном файле errno.h).

После того как файл открыт, в него можно записывать информацию или считывать информацию, в зависимости от режима.

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

int fclose(FILE*f);

Изменить режим работы с файлом можно только после закрытия файла.

Пример:

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

void main()

{

FILE *f;

char filename[20];

cout<<”\nEnter the name of file:”; cin>>filename;

if(f=fopen(filename,”rb”)==0)//открываем для чтения в бинарном режиме и проверяем

// возникает ли ошибка при открытии файла

{perror(strcat“error in file :”,filename);//strcat складывает две строки

exit(0);//выход из программы}

. . . . .

fclose(f);}

Для текстового файла:

if(f=fopen(filename,”rt”)==0)//открываем для чтения и проверяем возникает ли ошибка при //открытии файла

if(f=fopen(filename,”r”)==0)//открываем для чтения и проверяем возникает ли ошибка при //открытии файла

 

Если данный файл открывается или создается в текстовом режиме, вы можете приписать символ t к значению параметра type (rt, w+t, и т.д.); аналогично, для спецификации бинарного режима вы можете к значению параметра type добавить символ b (wb, a+b, и т.д.).

Если в параметре type отсутствуют символы t или b, режим будет определяться глобальной переменной _fmode. Если переменная _fmode имеет значение O_BINARY, файлы будут открываться в бинарном режиме, иначе, если _fmode имеет значение O_TEXT, файлы открываются в текстовом режиме. Данные константы O_... определены в файле fcntl.h.

При открытии файла в режиме обновления (UPDATE), над результирующим потоком stream могут быть выполнены как операции ввода, так и вывода. Тем не менее вывод не может следовать непосредственно за вводом без вмешательства функций fseek или rewind. Также ввод, без применения функций fseek, rewind не может непосредственно следовать за выводом или вводом, который встречает конец файла (EOF).

Пример:

#include <stdio.h>

 int main(void)

 { FILE *in, *out;

if((in = fopen("\\AUTOEXEC.BAT","rt"))==NULL)

{ fprintf(stderr, "Не могу открыть файл \n");

return(1); }

if((out = fopen("\\AUTOEXEC.BAK","wt"))==NULL)

{ fprintf(stderr, "Не могу открыть выходной файл \n");

return(1); }

 while(!feof(in))

fputc(fgetc(in), out);

 fclose(in);

 fclose(out);

 return 0; }

 

Функция fclose

Функция #include <stdio.h>

 int fclose (FILE * stream);

закрывает указанный поток stream. Все буфера, связанные с потоком stream, перед закрытием сбрасываются. Буфера, размещенные системой, освобождаются во время процесса закрытия. Буфера, назначенные функциями setbuf или setvbuf, не освобождаются автоматически. (Однако, если в качестве указателя, функции setvbuf передать 0, то он будет освобожден при закрытии потока.)

Функция fclose при успешном завершении возвращает 0. Если были обнаружены какие-либо ошибки, функция fclose возвращает значение EOF.

 

Функция fcloseall

Функция

       #include <stdio.h>

       int fcloseall (void)

закрывает все открытые потоки ввода-вывода. за исключением stdin, stdout, stderr, stdaux, stdprn. Возвращает общее число потоков, закрытых функцией, либо EOF, если обнаружены какие-либо ошибки при закрытии файлов.

Функция feof

 Функция      

  #include<stdio.h>

        int feof(FILE * stream);

является макрокомандой, которая производит проверку данного потока stream на признак конца файла (EOF). Если признак получил значение хотя бы один раз, то операции чтения файла сохраняют это значение до тех пор, пока не будет вызвана функция rewind или файл не будет закрыт. Признак конца файла сбрасывается при каждой операции ввода.

Функция feof возвращает ненулевое значение, если при последней операции ввода потока stream был обнаружен конец файла, и 0 в противном случае.

Функция freopen

Функция

       #include <stdio.h>

       FILE*freopen (const char*filename, const char*mode, FILE*stream)

закрывает поток, на описание которого указывает stream, а затем открывает файл, на спецификацию которого указывает ASCIIZ-строка filename. Режим открытия файла задает ASCIIZ- строка mode. Символы, формирующие режим, приведены в табл.3.1. В случае успеха функция возвращает указатель на описание открытого потока, которое будет совпадать с stream. В противном случае функция возвращает NULL. Повторное открытие того же самого файла изменяет права доступа, очищает внутренние буферы и позиционирует указатель записи-чтения либо на начало файла (r, w, r+, w+), либо на его конец (а, а+). Если файлы разные, переоткрытие приводит к переадресации файлового ввода-вывода. Эта функция обычно применяется для переадресации предоткрытых потоков stdin, stdout, stderr, stdaux, stdprn в файлы, определяемые пользователем.

Функция ftmpfile

Функция

      #include<stdio.h>

      FILE*tmpfile (void)

Создает и открывает временный файл в текущей директории текущего накопителя для потокового ввода-вывода. Файл открывается с правами “w+b”. В случае успеха функция возвращает указатель на начало описания открытого потока. В противном случае возвращается NULL. Отсутствует возможность определить имя созданного файла. После завершения программы или закрытия файла он автоматически удаляется. Функция используется тогда, когда программе необходимо временно переместить промежуточные данные на диск.

 

Функция fdopen

 Функция      

#include<stdio.h>

FILE * fdopen(int handle, char * type);

связывает поток с дескриптором, полученным функциями creat, dup, dup2 или open. Тип потока должен совпадать с режимом, в котором был открыт handle(см. таблицу 1).

При успешном завершении fdopen возвращает значение заново открытый поток stream. В случае ошибки, функция возвращают ноль (NULL).

 

Функция eof

 Функция     

#include<io.h>

  int eof(int handle);

 определяет, достигнут ли конец файла, связанного с дескриптором handle.

 Если текущая позиция является концом файла, функция eof возвращает значение 1; в противном случае значение 0. При возникновении ошибки возвращается значение -1, и глобальная переменная errno получает значение: EBADF - Неверный номер файла.

 

Функция ferror

 Функция     

#include<stdio.h>

       int ferror(FILE * stream);

является макрокомандой, которая проверяет данный поток stream на ошибочную операцию записи или чтения. Если установлен признак ошибки потока stream, он сохраняет его до вызова функций clearerr или rewind или до момента закрытия потока.

Функция ferror возвращает ненулевое значение, если в потоке stream была обнаружена ошибка.

 

Функция fflush

 Функция      

#include<stdio.h>

       int fflush(FILE * stream);

записывает в файл содержимое буфера, связанного с потоком stream, если он был открыт на вывод. Функция fflush не оказывает влияния на не буферизованные потоки.

В случае успешного завершения возвращает 0. При ошибке возвращает EOF.

 

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

Когда программа начинает выполняться, автоматически открываются несколько потоков, из которых основными являются:

- стандартный поток ввода (stdin);

- стандартный поток вывода (stdout);

- стандартный поток вывода об ошибках (stderr).

По умолчанию stdin ставится в соответствие клавиатура, а потокам stdout и stderr - монитор. Для ввода-вывода с помощью стандартных потоков используются функции:

 - getchar()/putchar() – ввод-вывод отдельного символа;

- gets()/puts() – ввод-вывод строки;

- scanf()/printf() – форматированный ввод/вывод.

Функции рассматривались, когда мы рассматривали строковые и символьные данные. Теперь мы можем связать их со стандартными потоками: ввод осуществляется из стандартного потока stdin вывод осуществляется в стандартный поток stdout. Аналогично работе со стандартными потоками выполняется ввод-вывод в потоки, связанные с файлами.

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

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

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

- int fputc(int c, FILE*fp), где fp – указатель на поток, в который выполняется запись, c – переменная типа int, в которой содержится записываемый в поток символ. Функция возвращает записанный в поток fp символ в форме int . Если символ не может быть записан, то возвращается значение EOF.

 

Функция ungetc

Функция

#include <stdio.h>

int ungetc(int c, FILE *stream);

возвращает символ c в заданный поток ввода stream. Stream должен быть буферизованным и открытым для чтения. Последующая операция чтения из stream начинается с символа c. EOF вернуть в поток посредством использования ungetc нельзя. Эта функция возвращает значение ошибки, если из stream ничего не прочитано или если c назад не возвращен. Символы, размещаемые в потоке посредством ungetc, могут быть уничтожены, если функции fseek или rewind вызваны перед считыванием символа из stream.

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

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

1) char* fgets(char* s,int n,FILE* f), где

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

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

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

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

2) int puts(char* s, FILE* f), где

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

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

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

Пример:

//копирование файла in в файл out

int MAXLINE=255;//максимальная длина строки

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);//закрыть оба файла

 

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

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

1) int fread(void*ptr,int size, int n, FILE*f), где

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

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

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

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

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

2) int fwrite(void*ptr,int size, int n, FILE*f), где

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

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

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

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

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

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

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

1) int fprintf(FILE *f, const char*fmt,. . .) , где

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

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

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

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

2) 1) int fscanf(FILE *f, const char*fmt, par1,par2, . . .) , где

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

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

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

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

Прямой доступ к файлам

Рассмотренные ранее средства обмена с файлами позволяют записывать и считывать данные только последовательно. Операции чтения/записи всегда производятся, начиная с текущей позиции в потоке. Начальная позиция устанавливается при открытии потока и может соответствовать начальному или конечному байту потока в зависимости от режима открытия файла. При открытии потока в режимах “r” и “w” указатель текущей позиции устанавливается на начальный байт потока, при открытии в режиме “a” - за последним байтом в конец файла. При выполнении каждой операции указатель перемещается на новую текущую позицию в соответствии с числом записанных/прочитанных байтов.

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

int fseek(FILE *f, long off, int org), где

FILE *f - – указатель на файл,

long off – позиция смещения

int org – начало отсчета.

Смещение задается выражение или переменной и может быть отрицательным, т. е. возможно перемещение как в прямом, так и в обратном направлениях. Начало отсчета задается одной из определенных в файле <stdio.h> констант:

SEEK_SET ==0 – начало файла;

SEEK_CUR==1 – текущая позиция;

SEEK_END ==2 – конец файла.

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

Примеры:

fseek(f,0L,SEEK_SET); //перемещение к началу потока из текущей позиции

fseek(f,0L,SEEK_END); //перемещение к концу потока из текущей позиции

fseek(f,-(long)sizeof(a),SEEK_SET); //перемещение назад на длину переменной а.

Кроме этой функции, для прямого доступа к файлу используются:

long tell(FILE *f);//получает значение указателя текущей позиции в потоке;

void rewind(FILE *f);//установить значение указателя на начало потока.

Функция filelength

 Функция      

#include<io.h>

long filelength(int handle);

возвращает длину в байтах файла, соответствующего дескриптору handle.

При успешном завершении функция filelength возвращает значение типа long- длину файла в байтах. При ошибке функция возвращает значение -1L, и глобальной переменной errno присваивается: EBADF - Неверный номер файла.

 

Функция fileno

 Функция      

#include<stdio.h>

int fileno(FILE * stream);

представляет собой макрокоманду, которая возвращает логический номер файла для заданного потока stream. Если поток stream имеет более одного номера, функция fileno возвращает номер, назначенный данному потоку при первом открытии.

Функция fileno возвращает целое число, обозначающее значение дескриптор файла, соответствующий потоку stream.

 

Функция flushall

 Функция      

    #include<stdio.h>

      int flushall(void);

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

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

 

Функция dup

 Функция      

#include<io.h>

int dup(int handle);

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

- тот же самый открываемый файл или устройство;

- тот же самый адресный указатель файла (то есть изменение одного адресного указателя приводит к изменению другого);

-тот же самый метод доступа (чтение, запись, чтение/запись).

Переменная handle получают значения при вызове функций creat,_creat,_open, open, dup, dup2.

При успешном завершении функция dup возвращает целое неотрицательное число, обозначающее соответствующий дескриптор файла; в противном случае функция dup возвращает значение -1.

При возникновении ошибки глобальная переменная errno получает одно из следующих значений:

                   EMFILE - Слишком много открытых файлов.

                   EBADF - Неверный номер файла.

 

Функция dup 2

 Функция     

  #include<io.h>

    int dup2(int oldhandle, int newhandle);

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

- тот же самый открываемый файл или устройство;

- тот же самый адресный указатель файла (то есть изменение одного адресного указателя приводит к изменению другого);

-тот же самый метод доступа (чтение, запись, чтение/запись).

dup2 создает новый дескриптор со значением newhandle Если файл связанный с дескриптором newhandle открыт, то при вызове dup2 он закрывается.

Переменная newhandle и oldhandle - это дескрипторы файлов, и получают значения при вызове функций creat,_creat,_open, open, dup, dup2.

При успешном завершении функция dup2 возвращает 0, значение      в противном случае функция dup2 возвращает значение -1.

При возникновении ошибки глобальная переменная errno получает одно из следующих значений:

                   EMFILE - Слишком много открытых файлов.

                   EBADF - Неверный номер файла.

 

Функция fread

 Функция     

    #include <stdio.h>

     size_t fread(void *ptr, size_t size, size_t n, FILE * stream);

считывает n элементов данных, каждый длиной size байтов, из потока stream в блок с адресной ссылкой ptr. Общее число вводимых байт равно n x size.

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

В случае достижения конца файла или возникновения ошибки функция fread возвращает short count (возможно 0).

 

Функция fwrite

 Функция     

#include <stdio.h>

size_t fwrite(void * ptr, size_t size, size_t n, FILE * stream);

добавляет n элементов данных, каждое величиной size байт в данный выходной поток. Данные записываются из ptr. Общее число выведенных байт равно n*size. ptr должен быть объявлен как указатель на некоторый объект.

При успешном завершении fwrite возвращает число выведенных элементов (не байт). При ошибке она возвращает меньшее число.

 

Функция fseek

 Функция      

    #include <stdio.h>

       int fseek(FILE * stream, long offset, int fromwhere);

устанавливает адресный указатель файла, соответствующий потоку stream, в новую позицию, которая расположена по смещению offset относительно места в файле, определяемого параметром fromwhere.

Параметр fromwhere может иметь одно из трех значений 0, 1 или 2, которые представлены тремя символическими константами (определенными в файле stdio.h), следующим образом:

                   ---------------------------------------------------

                   Параметр          Размещение в файле

                   fromwhere

                   --------------------------------------------------

                   SEEK_SET (0)  начало файла;

                   SEEK_CUR (1) позиция текущего указателя

                                                      файла;

                   SEEK_END (2) конец файла (EOF);

                   --------------------------------------------------

 

Функция fseek сбрасывает любой символ, записанный с помощью функции ungetc.

Функция fseek используется с операциями ввода/вывода в поток. При работе с дескриптором файла пользуйтесь функцией lseek.

После этой операции можно производить как ввод, так и вывод в поток.

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

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

Функция fsetpos

 Функция      

#include<stdio.h>

       int fsetpos(FILE *stream, const fpos_t *pos);

устанавливает указатель текущей позиции файла, связанного с потоком stream в новую позицию, которая определяется значением, получаемым предшествующим вызовом функции fgetpos. fsetpos также сбрасывает признак конца файла, но не оказывает влияния на символы, помещаемые в поток функцией ungetc. После вызова fsetpos можно производить как операции ввода, так и вывода.

При успешном завершении fsetpos возвращает 0. При возникновении ошибки она возвращает ненулевое значение и устанавливает errno не равным 0.

 

Функция fstat

 Функция      

#include <sys\stat.h>

       int fstat(char * handle, struct stat * buff)

записывает информацию об открытом файле (или директории), связанным с дескриптором handle в структуру stat. Aргумент buff адресует структуру stat (определенную в файле sys\stat.h).

Структура содержит следующие поля:

st_mode - битовая маска, дающая информацию о режиме открытия файла;

st_dev - идентификатор дисковода, на котором был открыт файл, или номер handle, если файл находится на устройстве;

st_rdev - так же, как и st_dev;

st_nlink - присваевается целая константа 1;

st_size - размер открытого файла в байтах;

st_atime - ближайшее время открытия файла, во время которого он был модифицирован;

st_mtime - так же, как и st_atime;

st_ctime - так же, как и st_atime.

Структура stat содержит на три поля больше, чем перечислено выше, они содержат данные, которые под управлением DOS не имеют значения.

Битовая маска, дающая информацию о режиме открытого файла, содержит следующие биты.

Один из следующих битов должен быть установлен:

S_IFCHR - установлен, если параметр handle указывает на устройство (fstat);

S_IFREG - установлен, если обычный файл определяется параметром handle

Один или оба следующих бита должны быть установлены:

S_IWRITE - установлен, если пользователь имеет разрешение на запись;

S_IREAD - установлен, если пользователь имеет разрешение на чтение.

 

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

Если информация об открытом файле была успешно получена, функция возвращают значение 0. В случае ошибки (невозможно получить информацию), функция возвращает -1 и присваивают переменной errno: EBADF - Неверный номер файла.

 

Функция ftell

 Функция     

#include <stdio.h>

       long int ftell(FILE *stream);

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

Значение, возвращаемое ftell, можно в дальнейшем использовать при вызове функции fseek.

Функция ftell возвращает положение указателя текущей позиции при успешном завершении. При ошибке возвращает-1L, и присваивает переменной errno положительное значение.

Варианты заданий

 


Дата добавления: 2019-02-22; просмотров: 247; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!