Библиотечные функции работы с файлами

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

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

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

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

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

Со времени принятия стандарта языка С++, что окончательно произошло в 1989 году, в системах программирования С++ такие необоснованные расширения библиотек практически исчезли, но в ранних версиях языка подобный разнобой был очень велик. К сожалению, наборы функций второй категории не согласованы даже для различных систем программирования в рамках одной операционной системы на одном типе архитектур компьютеров. Это четко прослеживается на примере систем программирования Turbo C от Borland и Microsoft C: библиотечные функции, обеспечивающие интерфейс для вызова одной и той же функции операционной системы, могут иметь не только разные параметры, но и разные названия.

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

Понятие потока

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

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

байт0 байт1 байт2……………………………EOF

EOF является стандартной константой – признаком конца файла.

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

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

В языке Си существует два типа потоков: текстовый (text) и двоичный (binary).

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

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

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

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

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

Каждый поток, связанный с файлом, имеет управляющую структуру, называемую FILE. Она описана в заголовочном файле stdio.h.

В файловой системе языка Си в начале выполнения каждой программы автоматически открываются 5 предопределенных потоков. Три из ник относятся к стандартным потокам: stdin – стандартный поток ввода, stdout – стандартный поток вывода, stderr – стандартный поток ошибок. Обычно эти потоки направляются к консоли (как известно, консоль это клавиатура+дисплей). Но можно перенаправлять их на другое устройство, в тех операционных системах, которые поддерживают перенаправление. Перенаправление ввода/вывода поддерживаются такими операционными системами, как Windows, DOS, UNIX, OS/2.

Стандартные потоки в C++

Выполнение любой программы С++ начинаются с четырьмя предопределенными открытыми потоками, объявленными как объекты классов _withassign в iostream.h следующим образом:

extern istream_withassign cin; extern ostream_withassign cout; extern ostream_withassign cerr; extern ostream_withassign clog;

Их конструкторы вызываются всякий раз при включении iostream.h, но фактическая инициализация выполняется только один раз.

Все эти предопределенные стандартные потоки по умолчанию связаны с терминалом.

Четыре стандартных потока предназначены для:

cin - стандартного ввода;

cout - стандартного вывода;

cerr - стандартного вывода ошибок;

clog - полностью буферизованного вывода ошибок.

В табл. 11.1 приведено назначение классов потокового ввода-вывода.

Таблица 11.1

Назначение классов потокового ввода-вывода

ios Потоковый базовый класс
Потоковые классы ввода
istream Потоковый класс общего назначения для ввода, являющийся базовым классом для других потоков ввода
ifstream Потоковый класс для ввода из файла
istream with assign Потоковый класс ввода для cin
istrstream Потоковый класс для ввода строк
Потоковые классы вывода
ostream Потоковый класс общего назначения для вывода, являющийся базовым классом для других потоков вывода
ofstream Потоковый класс для вывода в файл
ostream_withassign Потоковый класс ввода для cout, cerr, and clog
ostrstream Потоковый класс для вывода строк
Потоковые классы ввода-вывода
iostream Потоковый класс общего назначения для ввода-вывода, являющийся базовым классом для других потоков ввода-вывода
fstream Потоковый класс для ввода-вывода в файл
sir stream Потоковый класс для ввода-вывода строк
stdiostream Класс для ввода-вывода в стандартные файлы ввода-вывода
Классы буферов для потоков
Streambuf Абстрактный базовый класс буфера потока
filebuf Класс буфера потока для дисковых файлов
s trs treambuf Класс буфера потока для строк
stdiobuf Класс буфера потока для стандартных файлов ввода-вывода

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

Потоки ввода-вывода C++ предоставляют некоторые преимущества по сравнению с функциями ввода-вывода библиотеки С.

Функции работы с потоками

В этом разделе мы опишем пять основных функций для работы с потоками.

Функция pthread_create

При запуске пpoгрaммы вызовом exec создается единственный поток, называемый начальным потоком, или главным (initial thread). Добавочные потоки создаются вызовом pthread_create:

#include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg); /* Возвращает 0 в случае успешного завершения, положительное значение Еххх – в случае ошибки */  

 

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

Каждый поток обладает некоторым количеством атрибутов: приоритетом, начальным размером стека, признаком демона и т. п. При создании потока эти атрибуты могут быть указаны с помощью переменной типа pthread_attr_t, значение которой имеет более высокий приоритет, чем значения по умолчанию. Обычно мы используем значения по умолчанию. При этом аргумент attr является нулевым указателем.

Наконец, при создании потока должны быть указать функцию, которую он будет выполнять, — начальную функцию потока (thread start function). Поток запускается вызовом этой функции и завершается либо явно (вызовом pthread_exit), либо неявно (возвратом из этой функции). Адрес функции указывается в аргументе func, и вызывается она с единственным аргументом — указателем arg. Если функции нужно передать несколько аргументов, следует упаковать их в структуру и передать ее адрес в качестве единственного аргумента начальной функции.

Функции Posix для работы с потоками обычно возвращают 0 в случае успешного завершения работы и ненулевое значение в случае ошибки. В отличие от большинства системных функций, возвращающих –1 в случае ошибки и устанавливающих значение errno равным коду ошибки, функции Pthread возвращают положительный код ошибки. Например, если pthread_create не сможет создать новый поток из-за превышения системного oгрaничeния на потоки, эта функция вернет значение EAGAIN. Функции Pthread не устанавливают значение переменной errno. Несоответствий при их вызове не возникает, поскольку ни один из кодов ошибок не имеет нулевого значения (<sys/errno.h>).

Функция pthread_join

Вызвов pthread_join - завершения какого-либо процесса. Сравнивая потоки с процессами Unix, можно сказать, что pthread_create аналогична fork, a pthread_join — waitpid:

#include <pthread.h> int pthread_join(pthread_t tid, void **status); /* Возвращает 0 в случае успешного завершения, положительное значение Еххх – в случае ошибки */  

К сожалению, невозможно задать режим ожидания завершения нескольких потоков (аналога waitpid с идентификатором процесса –1 нет).

Если указатель status ненулевой, возвращаемое потоком значение (указатель на объект) сохраняется в ячейке памяти, на которую указывает status.

Функция pthread_self

У каждого потока имеется свой идентификатор, уникальный в пределах данного процесса. Идентификатор возвращается pthread_create и используется при вызове pthread_join. Поток может узнать свой собственный идентификатор вызовом pthread_self:

#include <pthread.h> pthread_t pthread_self(void); /* Возвращает идентификатор вызвавшего потока */ Вызов pthread_self является аналогом getpid для процессов Unix.    

 

Функция pthread_detach

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

Функция pthread_detach делает данный поток отсоединенным:

#include <pthread.h> int pthread_detach(pthread_t tid); /* Возвращает 0 в случае успешного завершения, положительное значение Еххх в случае ошибки */    

 

Эта функция вызывается потоком при необходимости изменить собственный статус в форме

pthread_detach(pthread_self());

Функция pthread_exit

Одним из способов завершения потока является вызов pthread_exit:

#include <pthread.h> void pthread_exit(void *status); /* ничего не возвращает вызвавшему потоку */

 

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

Указатель status не должен быть установлен на локальный объект вызвавшего потока (типа автоматической переменной), поскольку этот объект уничтожается при завершении потока.

Поток может быть завершен двумя другими способами: 1)начальная функция потока (третий аргумент pthread_create) может вызвать return. Поскольку эта функция должна объявляться как возвращающая указатель на тип void, это возвращаемое значение становится статусом завершения потока; 2)функция main процесса может завершить работу или один из потоков может вызвать exit или _exit. При этом процесс завершает работу немедленно, вместе со всеми своими потоками.

Список использованной литературы:

а) основная литература:

1) М.И. Болски. Язык программирования Си. 1988г.

2)Б.И. Березин. Начальный курс С и С++. М.,2001г.

б) дополнительная литература:

3)Т. А. Павловская C/C++ Программирование на языке высокого уровня. Год издания: 2012.

4) Литвиненко Н. А. - Технология программирования наС++. Год издания: 2013. Издательство: БХВ-Петербург.

5.)ейтел Х.М, Дейтел П.Д. Как программировать на С: пер. с англ. – М.: БИНОМ, 2014.— 908 с.: ил.

6) Шилдт Г. Полный справочник по C++: Пер. с англ. – М.: Вильямс, 2013 .— 699 с

7) Коплиен Дж. - Мультипарадигменное проектирование для C++.

8) Богуславский Ан.А., Соколов С.М. Основы программирования на языке Си++: Для студентов физико-математических факультетов педагогических институтов. Часть I. Введение в программирование на языке Си++. - Коломна: КГПИ, 2012. - 490 с.

9) Язык С++. Учебное пособие И. Ф. Астахова, С. В. Власов, В. В. Фертиков, А. В. Ларин

10) Степанов Е.О., Чириков С.В. Стиль программирования на C++. Учебное пособие. - СПб.: СПбГИТМО(ТУ), 2015. - 48 с.

Знания следует самостоятельно проверить путем ответов на контрольные вопросы.