- •29 Марта 2005 г., протокол № 5
- •Введение
- •1. Классы. Первое знакомство
- •2. Классы и функции
- •Указатель this
- •3. Наследование, виртуальные функции и полиморфизм
- •4. Массивы объектов, указатели и ссылки
- •5. Шаблоны и исключительные ситуации
- •6. Потоки и классы ввода/вывода
- •7. Статические и константные члены, локальные классы
- •Объектно-ориентированное
- •220050, Минск, пр. Независимости, 4.
- •220004, Минск, ул. Освобождения, 9.
6. Потоки и классы ввода/вывода
Стандартные потоки
В С++ ввод и вывод осуществляется через потоки – объекты классов ввода/вывода, которые передают и принимают данные и связываются с физическими устройствами. Следующие потоки определены и автоматически открываются при запуске приложения:
extern istream cin; // cтандартный поток ввода с клавиатуры
extern ostream cout; // cтандартный поток вывода на экран
extern ostream cerr; /* cтандартный поток вывода сообщений об ошибках */
extern ostream clog;/*буферизованный поток вывода сообщений об ошибках */
Рассмотрим пример:
#include <iostream.h>//пример 60
int main() {
int a,b,c;
cout << "Hello, world\n";
cin>>a>>b>>c;
cout << "a/b+c=" << a*b-c << "\n";
cout << "a^b|c=" << (a^b|c) << "\n";
return 0; }
Операция << пишет аргумент, строку "Hello, world" в стандартный поток вывода cout. Ввод производится с помощью операции >> стандартного потока ввода cin. Приоритет операции вставки в поток << достаточно низок, чтобы не использовать скобки для арифметических выражений. Для операций с более низкими приоритетами без скобок не обойтись.
Форматирование
Операция << применяется для неформатированного вывода данных стандартных типов. Операция определяет тип данных и выбирает подходящий формат. То же происходит и с операцией извлечения из потока >>. Помимо этого существует несколько функций, преобразующих параметр в строку, которая используется для вывода.
char* oct(long, int=0); // восьмеричное представление
char* dec(long, int=0); // десятичное представление
char* hex(long, int=0); // шестнадцатеричное представление
char* chr(int, int=0); // символ
char* str(char*, int=0); // строка
Второй (необязательный) параметр указывает, сколько символьных позиций должно использоваться. Если задано поле нулевой длины, то для вывода потребуется столько позиций, сколько нужно, иначе будет производиться усечение или дополнение. Например:
cout <<"dec("<< x << ")=oct("<< oct(x,6)<<")= hex("<< hex(x,4)<< ")";
Если x==15, то в результате получится:
dec(15) = oct( 17) = hex( f);
Для преобразования при выводе можно использовать строку в формате:
char* form(char* format, список параметров);
Аналогично стандартной функции printf() вывода языка C функция form() возвращает строку, получаемую в результате форматирования параметров, стоящих после первого управляющего параметра – строки формата format. Строка формата состоит из обычных символов, которые просто копируются в поток вывода, и спецификаций преобразования, влекущих преобразование и вывод параметра. Каждая спецификация преобразования начинается с символа %. Например: cout<<form(" х= %d ",х);
Манипуляторы
Манипуляторы – функции потока, которые можно включать в операции помещения и извлечения в потоки (<<, >>), они бывают следующие:
endl // помещение в выходной поток символа конца строки '\n'
ends // помещение в выходной поток символа '\0'
flush // вызов функции вывода буферизованных данных
dec, hex, oct // установка основания системы счисления
ws // игнорирование при вводе пробелов
setbase(int) // установка основания системы счисления 10,8,16
resetiosflasg(long) // сброс флагов форматирования по маске
setiosflags(long) // установка флагов форматирования по маске
setfill(int) // установка заполняющего символа
setprecision(int)/*установка точности вывода вещественных чисел */
setw(int) // установка ширины поля ввода-вывода
Пример вызова манипулятора:
cout << 15 << hex << 15 << setbase(8) << 15;
cout<<hex<<100<<oct<<100<<dec<<100;
Ошибки потоков
Каждый поток (istream или ostream) имеет ассоциированное с ним состояние, с помощью проверки которого осуществляется обработка ошибок и нестандартных условий.
Поток может находиться в одном из следующих состояний:
enum stream_state { _good, _eof, _fail, _bad };
Если состояние _good или _eof, последняя операция ввода прошла успешно, а если – _good, то следующая операция ввода может пройти успешно, в противном случае она закончится неудачей. Другими словами, применение операции ввода к потоку, который не находится в состоянии _good, является пустой операцией. Состояние потока можно проверять, например, так:
switch (cin.rdstate()) {
case _good: // последняя операция над cin прошла успешно
break;
case _eof: // конец файла
break;
case _fail: // некоего рода ошибка форматирования
break;
case _bad: // возможно, символы cin потеряны
break; }
Файловый ввод-вывод с применением потоков С++
В С++ существуют классы потоков ввода-вывода, определенные в соответствующей библиотеке:
ios //базовый потоковый класс
streambuf //буферизация потоков
istream //потоки ввода
ostream //потоки вывода
iostream //двунаправленные потоки
istrstream //строковые потоки ввода
ostrstream //строковые потоки вывода
strstream //двунаправленные строковые потоки
ifstream //файловые потоки ввода
ofstream //файловые потоки вывода
fstream //двунаправленные файловые потоки
Стандартные потоки (istream,ostream,iostream) служат для работы с терминалом. Строковые потоки (istrstream, ostrstream, strstream) – для ввода-вывода из строковых буферов, размещенных в памяти, файловые потоки (ifstream, ofstream, fstream) – для работы с файлами.
Для реализации файлового ввода-вывода небходимо включить заголовочный файл fstream.h, содержащий производные от istream и ostream классы ifstream, ofstream и fstream, и объявить соответствующие объекты. Например:
ifstream in;//ввод
ofstream out;//вывод
fstream io;//ввод - вывод
Файловые потоки можно определить с помощью конструкторов:
ofstream obj (filename, mode),
ifstream obj (filename, mode),
где mode может иметь следующие значения:
ios::app //запись в конец существующего файла
ios::ate //после открытия файла перейти в его конец
ios::binary /*открыть файл в двоичном режиме (по умолчанию – текстовый) */
ios::in //открыть для чтения
ios::nocreate //сообщать о невозможности открытия, если файл не существует
ios::noreplace //сообщать о невозможности открытия, если файл существует
ios::out //открыть для вывода
ios::trunc //если файл существует, стереть содержимое
При необходимости изменения способа открытия или применения файла можно при создании файлового потока использовать два или более флагов: ios::app|ios::noreplace
Для открытия файла одновременно для чтения и записи используются объекты класса fstream:
fstream obj(filename, ios::in|ios::app);
После объявления потоков открытие файла, связывающее его с потоком, можно производить не через конструктор, а с помощью метода open():
void open (char *filename,int mode,int access), где filename – имя файла, включающее путь; mode – режим открытия файла, access – доступ. Параметр аccess принимает следующие значения: 0 – файл со свободным доступом, 1 –только для чтения, 8 – архивный файл. Например:
ofstream out;
out.open(“test.dat”,ios::out,0);
Параметры можно задать по умолчанию.
оut.open(“test.dat”);//будет то же самое
При завершении программы открытые файлы неявно закрываются. Для явного закрытия объектов файловых потоков применяется метод close().
Ввод-вывод в файлы
Для чтения-записи в потоки можно использовать перегружаемые операторы-функции >> и <<. Например:
#include <fstream.h>//пример 61
int main(){
ofstream fout("test"); /* создание файла вывода c помощью конструктора */
if(!fout) { cout << "Файл открыть невозможно\n";
return 1; }
fout << "Привет!\n";
fout << 100 << ' ' << hex << 100 << endl;
fout.close();
ifstream fin("test"); // открытие обычного файла ввода
if(!fin) {cout << "Файл открыть невозможно\n";return 1;}
char str[80];
int i;
fin >> str >> i;
cout << str << ' ' << i << endl;
fin.close();
return 0; }
Сам оператор << можно перегрузить, как в следующем примере:
/*создание недружественной функции-вставки для объектов Coord */
#include <iostream.h>//пример 62
class Coord {
public:
int x,y;//должны быть открытыми
Coord() {x=0;y=0;}
Coord(int i,int j) {x=i;y=j;}
};
//функция вставки для объектов класса coord
ostream &operator<<(ostream &stream, Coord ob){
stream <<ob.x<<”,”<<ob.y<<’\n’;
return stream;
}
int main(){
Coord a(1,1), b(10,23);
cout <<a<<b;
return 0; }
Оператор ввода (извлечения) также можно перегрузить:
istream &operator>>(istream &stream, имя_класса ob){
//тело функций ввода
return stream;
}
При работе с файлами возможно использование методов ввода-вывода одного символа: istream &get(char &ch); и ostream &put(char ch);
Пример использования этих методов и аргументов командной строки:
#include <iostream.h>//пример 63
#include <fstream.h>
int main(int argc, char *argv[ ]) {
char ch;
if(argc!=2) {
cout << "Использование:WRITE<имя_файла>\n";
return 1;}
ofstream out(argv[1]);
if(!out) {cout << "Файл открыть невозможно\n";return 1; }
cout << "Для остановки введите символ $\n";
do {
cout << ": ";
cin.get(ch);
out.put(ch);
} while (ch!='$');
out.close();
return 0; }
Для записи-считывания блоков двоичных данных используются функции, которые считывают-записывают n байт в буфер или из буфера:
istream &read(unsigned char *buf, int n);
ostream &write(const unsigned char *buf, int n);
#include <iostream.h>//Пример 64
#include <fstream.h>
#include <string.h>
int main() {
ofstream out("test", ios::binary);/* не будет нежелательных проебразований символов при вводе и выводе*/
if(!out) {cout << "Файл открыть невозможно\n";return 1;}
double num = 100.45;
char str[ ] = "Это проверка";
out.write((char *) &num, sizeof(double));
out.write(str, strlen(str));
out.close();
return 0; }
Для позиционирования в файле имеются методы seekg(), seekp(), tellp(). При этом seekg() назначает или возвращает текущую позицию указателя чтения, а seekp() – текущую позицию указателя записи. Обе функции могут иметь один или два аргумента. При вызове с одним аргументом функции перемещают указатель на заданное место, а при вызове с двумя аргументами вычисляется относительная позиция от начала файла (ios::beg) текущей позиции (ios::cur) или от конца файла (ios::end). Текущая позиция определяется методом tellp().
Для объектов файловых потоков контроль состояния производится с помощью методов, манипулирующих флагами ошибок:
bad() – возвращает ненулевое значение, если обнаружена ошибка;
clear() – сбрасывает сообщения об ошибках;
eof() – возвращает ненулевое значение, если обнаружен конец файла;
fail() – возвращает ненулевое значение, если операция завершилась неудачно;
good() – возвращает ненулевое значение, если флаги ошибок не выставлены;
rdstate() – возвращает текущее состояние флагов ошибок.
Если флаги показывают наличие ошибки, все попытки поместить в поток новые объекты будут игнорироваться.
