- Lektsia - бесплатные рефераты, доклады, курсовые работы, контрольные и дипломы для студентов - https://lektsia.info -

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



istream &manip_name(istream &stream)

{

 // код манипуляторной функции

 return stream;

}

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

#include <iostream>

#include <iomanip>

using namespace std;

istream &prompt(istream &stream)

{

 cin >> hex;

 cout << "Введите число в шестнадцатеричном формате: ";

 return stream;

}

Int main()

{

 int i;

 cin >> prompt >> i;

 cout << i;

 return 0;

}

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

Файловый ввод-вывод

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

Как открыть и закрыть файл

В C++ файл открывается путем связывания его с потоком. Как вы знаете, существуют потоки трех типов: ввода, вывода и ввода-вывода. Чтобы открыть входной поток, необходимо объявить потоковый объект типа ifstream. Для открытия выходного потока нужно объявить поток класса ofstream. Поток, который предполагается использовать для операций как ввода, так и вывода, должен быть объявлен как объект класса fstream. Например, при выполнении следующего фрагмента кода будет создан входной поток, выходной и поток, позволяющий выполнение операций в обоих направлениях.

Ifstream in; // входной поток

Ofstream out; // выходной поток

Fstream both; // поток ввода-вывода

Чтобы открыть файл, используйте функцию open().

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

void ifstream::open(const char *filename, ios::openmode mode = ios::in);

void ofstream::open(const char *filename, ios::openmode mode = ios::out | ios::trunc);

void fstream::open(const char * filename, ios::openmode mode = ios::in | ios::out);

Здесь элемент filename означает имя файла, которое может включать спецификатор пути. Элемент mode определяет способ открытия файла. Он должен принимать одно или несколько значений перечисления openmode, которое определено в классе ios.

Ios::арр

Ios::ate

Ios::rbinary

Ios::in

Ios::out

Ios::trunc

Несколько значений перечисления openmode можно объединять посредством логического сложения (ИЛИ).

На заметку. Параметр mode для функции fstream::open() может не устанавливаться по умолчанию равным значению in | out (это зависит от используемого компилятора). Поэтому при необходимости этот параметр вам придется задавать в явном виде.

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

Значение ios::in говорит о том, что данный файл открывается для ввода данных, а значение ios::out обеспечивает открытие файла для вывода данных.

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

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

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

ofstream out;

out.open("тест");

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

Не открытый в результате неудачного выполнения функции open() поток при использовании в булевом выражении устанавливается равным значению ЛОЖЬ. Этот факт может служить для подтверждения успешного открытия файла, например, с помощью такой if-инструкции.

if(!mystream) {

 cout << "He удается открыть файл.";

 // обработка ошибки

}

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

Можно также проверить факт успешного открытия файла с помощью функции is_open(), которая является членом классов fstream, ifstream и ofstream. Вот ее прототип,

bool is_open();

Эта функция возвращает значение ИСТИНА, если поток связан с открытым файлом, и ЛОЖЬ — в противном случае. Например, используя следующий код, можно узнать, открыт ли в данный момент потоковый объект mystream.

if(!mystream.is_open()) {

 cout << "Файл не открыт.";

 // ...

}

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

ifstream mystream("myfile"); // файл открывается для ввода

Если по какой-то причине файл открыть невозможно, потоковая переменная, связываемая с этим файлом, устанавливается равной значению ЛОЖЬ.

Чтобы закрыть файл, вызовите функцию close().

Чтобы закрыть файл, используйте функцию-член close(). Например, чтобы закрыть файл, связанный с потоковым объектом mystream, используйте такую инструкцию,

mystream.close();

Функция close() не имеет параметров и не возвращает никакого значения.

Чтение и запись текстовых файлов

Проще всего считывать данные из текстового файла или записывать их в него с помощью операторов "<<" и ">>". Например, в следующей программе выполняется запись в файл test целого числа, значения с плавающей точкой и строки.

// Запись данных в файл.

#include <iostream>

#include <fstream>

using namespace std;

Int main()

{

 ofstream out("test");

 if(!out) {

  cout << "He удается открыть файл.";

  return 1;

 }

 out << 10 << " " << 123.23 << "";

 out << "Это короткий текстовый файл.";

 out.close();

 return 0;

}

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

// Считывание данных из файла.

#include <iostream>

#include <fstream>

using namespace std;

Int main()

{

 char ch;

 int i;

 float f;

 char str[80];

 ifstream in("test");

 if(!in) {

  cout << "He удается открыть файл.";

  return 1;

 }

 in >> i;

 in >> f;

 in >> ch;

 in >> str;

 cout << i << " " << f << " " << ch << "";

 cout << str;

 in.close();

 return 0;

}

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

Неформатированный ввод-вывод данных в двоичном режиме

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

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

Функция get() считывает символ из файла, а функция put() записывает символ в файл.

В общем случае существует два способа записи неформатированных двоичных данных в файл и считывания их из файла. Первый состоит в использовании функции-члена put() (для записи байта в файл) и функции-члена get() (для считывания байта из файла). Второй способ предполагает применение "блочных" С++-функций ввода-вывода read() и write(). Рассмотрим каждый способ в отдельности.

Использование функций get() и put()

Функции get() и put() имеют множество форматов, но чаще всего используются следующие их версии:

istream &get(char &ch);

ostream &put(char ch);

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

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

/* Отображение содержимого файла с помощью функции get().

*/

#include <iostream>

#include <fstream>

using namespace std;

int main(int argc, char *argv[])

{

 char ch;

 if(argc!=2) {

  cout << "Применение: имя_программы <имя_файла>";

  return 1;

 }

 ifstream in(argv[1], ios::in | ios::binary);

 if(!in) {

  cout << "He удается открыть файл.";

  return 1;

 }

 while(in) {

  /* При достижении конца файла потоковый объект in примет значение false. */

  in.get(ch);

  if(in) cout << ch;

 }

 in.close();

 return 0;

}

При достижении конца файла потоковый объект in примет значение ЛОЖЬ, которое остановит выполнение цикла while.

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

while(in.get(ch)) cout << ch;


Этот вариант также имеет право на существование, поскольку функция get() возвращает потоковый объект in, который при достижении конца файла примет значение false.

В следующей программе для записи строки в файл используется функция put().

/* Использование функции put() для записи строки в файл.

*/

#include <iostream>

#include <fstream>

using namespace std;

Int main()

{

 char *p = "Всем привет!";

 ofstream out("test", ios::out | ios::binary);

 if(!out) {

  cout << "He удается открыть файл.";

  return 1;

 }

 while(*p) out.put(*p++);

 out.close();

 return 0;

}

Считывание и запись в файл блоков данных

Чтобы считывать и записывать в файл блоки двоичных данных, используйте функции-члены read() и write(). Их прототипы имеют следующий вид.

istream &read(char *buf, streamsize num);

ostream &write(const char *buf, int streamsize num);

Функция read() считывает num байт данных из связанного с файлом потока и помещает их в буфер, адресуемый параметром buf. Функция write() записывает num байт данных в связанный с файлом поток из буфера, адресуемого параметром buf. Как упоминалось выше, тип streamsize определен как некоторая разновидность целочисленного типа. Он позволяет хранить самое большое количество байтов, которое может быть передано в процессе любой операции ввода-вывода.

Функция read() вводит блок данных, а функция write() выводит его.