Флаги и форматирующие методы

ВВЕДЕНИЕ

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

Классы стандартной библиотеки можно разделить на следующие группы.

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

Строковый класс – предназначены для работы с символьными переменными.

Контейнерные классы – реализуют структуры для хранения данных (списки, векторы, множества).

Итераторы – предназначены для унифицированного доступа к элементам классов.

Математические классы – поддерживают обработку массивов с плавающей точкой и работу с комплексными числами.

Диагностические классы – обеспечивают динамическую идентификацию типов и объектно-ориентированную обработку ошибок.

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

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

 


Потоковые классы

 

Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.

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

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

По виду устройств, с которыми работает поток, можно выделить стандартные, файловые и строковые потоки.

Стандартные потоки предназначены для передачи данных от клавиатуры и на экран.

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

Строковые потоки – для работы с массивами символов.

Для поддержки потоков библиотека С++ содержит иерархию классов, построенную на основе двух базовых классов – ios и sterambuf. Класс ios содержит общие для ввода и вывода поля и методы, класс steambuf обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами.

Далее перечисляются часто используемые классы потоков:

ios – базовый класс потоков;

istream – класс входных потоков;

ostream – класс выходных потоков;

iostream – класс двунаправленных потоков;

istringstream – класс входных строковых потоков;

ostringstream – класс выходных строковых потоков;

stringstream – класс двунаправленных потоков;

ifstream – класс входных файловых потоков;

ofstream – класс выходных файловых потоков;

fstream – класс двунаправленных файловых потоков.

Описание классов находится в заголовочных файлах:

<ios> – базовый класс ввода/вывода;

<iosfwd> – предварительные объявления средств ввода/вывода;

<istream> – шаблон потока ввода;

<ostream> – шаблон потока вывода;

<iostream> – стандартные объекты и операции с потоками ввода/вывода;

<fstream> – потоки ввода/вывода в файлы;

<sstream> – потоки ввода/вывода в строки;

<streambuf> – буферизация потоков ввода/вывода;

<iomanip>–манипуляторы (см. далее).

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

 

Заголовочный файл <iostream> помимо описания классов для ввода/вывода содержит четыре предопределенных объекта (табл.1).

 

Таблица 1

Предопределенные объекты

 

Объект Класс Описание
cin istream Связывается с клавиатурой (стандартным буферизованным вводом)
cout ostream Связывается с экраном (стандартным буферизованным выводом)

Окончание табл.1

cerr ostream Связывается с экраном (стандартным небуферизованным выводом), куда направляются сообщения об ошибках
clog ostream Связывается с экраном (стандартным буферизованным выводом), куда направляются сообщения об ошибках

 

 

В классах istream и ostream операции извлечения из потока >> и помещения в поток << определены путем перегрузки операций сдвига.

Числовые значения можно вводить в десятичной или шестнадцатеричной системе счисления (с префиксом 0x) со знаком или без знака. Вещественные числа представляются в форме с фиксированной точкой или плавающей точкой (с порядком).

При вводе строки извлечение происходит до ближайшего пробела (вместо него в строку записывается 0-символ).

Значения указателей вводятся в шестнадцатеричной системе счисления.

Форматирование данных

 

Форматирование в потоковых классах выполняется тремя способами: с помощью флагов, манипуляторов и форматирующих методов.

Флаги и форматирующие методы

Флаги – это отдельные биты, объединенные в поле x_flags типа long класса ios.

 

Таблица 2

Некоторые флаги форматирования

 

Флаг Умолчание Описание
skipws + При извлечении пробельные символы игнорируются
left - Выравнивание по левому краю поля
right + Выравнивание по правому краю поля
internal - Знак числа выводится по левому краю, число – по правому
dec + Десятичная система счисления
oct - Восьмеричная система счисления
hex - Шестнадцатеричная система счисления
showbase - Выводится основание системы счисления (0x – шестнадцатеричная, 0 – восьмеричная)
showpoint - При выводе числа печатается точка и дробная часть числа
uppercase - Использование при выводе верхнего регистра
scientific - Печать вещественных чисел в экспоненциальной форме
fixed - Печать вещественных чисел в форме с фиксированной точкой

 

Для управления флагами в классе ios есть методы flags, setf и unsetf.

long ios : : flags (long); – возвращает текущие флаги потока;

long ios : : flags (long); – присваивает флагам значение параметра;

long ios : : setf (long, long); – присваивает флагам, биты которых установлены в первом параметре, значение соответствующих битов второго параметра;

long ios : : setf (long, long); – устанавливает флаги, биты которых установлены в параметре;

long ios : : unsetf (long, long); – сбрасывает флаги, биты которых установлены в параметре;

Все функции возвращают прежние флаги потока.

Кроме флагов для форматирования используются поля класса ios:

int x_width – минимальная ширина поля;

int x_precision – количество цифр в дробной части числа;

int x_fill – символ заполнения поля вывода.

Для управления этими полями используются методы width, precision и fill:

int ios :: width ( ) – возвращает значение ширины поля вывода;

int ios :: width ( ) – устанавливает значение ширины поля вывода;

int ios :: precision ( ) – возвращает значение точности представления дробной части;

int ios :: precision (int ) – устанавливает значение точности представления дробной части;

char fill ( ) – возвращает текущий символ заполнения;

char fill (char) – устанавливает значение текущего символа заполнения (по умолчанию – пробел).

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

adjustfield (left | right | internal)

basefield ( dec | oct | hex)

floatfield (scientific | fixed)

Пример

# include <iostream.h>

int main ( ) {

long a = 100, b = 077;

cout.width (7); cout.unsetf (ios :: dec);

cout.setf (ios :: hex | ios :: showbase | ios::uppercase);

cout << a;

cout.width (7);

cout << b << endl;

double d = 0.12, c = 1.3e-4;

cout.setf(ios :: left);

cout << d << endl;

cout << c;

return 0;

}

 

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

0X3T8 0X3F

0.12

0.00013

Манипуляторы

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

Простые манипуляторы

dec – устанавливает флаг десятичной системы;

oct – устанавливает флаг восьмеричной системы;

hex – устанавливает флаг шестнадцатеричной системы;

ws – устанавливает при вводе извлечение пробельных символов;

endl – при выводе включает в поток символ новой строки и выгружает буфер;

ends – при выводе включает в поток нулевой символ;

flush – при выводе выгружает буфер.

Изменение системы счисления действует до следующего явного изменения.

Пример

cout << 13 << hex << ‘ ‘ << 13 << oct << ‘ ‘ << 13 << endl;

На экране:

13 d 15

Параметризованные манипуляторы

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

setbase (int n) – задает основание системы счисления (n = 8, 10, 16 или 0). 0 – основание по умолчанию (десятичное, кроме случаев, когда вводятся восьмеричные или шестнадцатеричные числа);

resetiosflags ( long) – сбрасывает флаги состояния потока, биты которых установлены в параметре;

setfill (int) – устанавливает символ-заполнитель с кодом, равным значению параметра;

setprecision (int) – устанавливает максимальное количество цифр в дробной части чисел;

setw (int) – устанавливает максимальную ширину поля вывода;

setiosflags (long) – устанавливает флаги состояния потока, биты которых в параметре равны 1.

 

Пример

double d (1.234);

cout << setfill (‘.’) << setprecision (4)

<< setiosflags (ios : : showpoint | ios : : fixed);

cout << setw (12) << d << endl;

На экране:

……1.2340

 

Методы обмена с потоком

 

В потоковых классах наряду с операциями извлечения >> и включения << определены методы для неформатированного чтения и записи в поток (при этом преобразования данных не выполняются).

 

Функция Назначение
Класс istream
gcount ( ) Возвращает количество символов, считанных с помощью последней функции неформатированного ввода
get ( ) Возвращает код извлеченного из потока символа или EOF
get (buf, num, lim = ‘\n’) Считывает num-1 символов (или пока не встретится символ lim) и копирует их в строку buf. Вместо символа lim в строку записывается признак конца строки (‘\0’). Символ lim остается в потоке. Возвращает ссылку на текущий поток
getline (buf, num, lim = ‘\n’) Аналогична get ( ), но копирует в buf и символ lim    

Продолжение таблицы

ignore (num = 1, lim = EOF) Считывает и пропускает символы до тех пор, пока не будет прочитано num символов (по умолчанию 1) или не встретится разделитель lim. Возвращает ссылку на текущий поток
peek ( ) Возвращает следующий символ без удаления его из потока или EOF, если достигнут конец файла
putback (c) Помещает в поток символ с, который становится текущим при извлечении из потока
read (buf, num) Считывает num символов (или все символы до конца файла, если их меньше num) в символьный массив buf и возвращает ссылку на текущий поток
readsome (buf, num) Считывает num символов в символьный массив buf и возвращает количество считанных символов
seekg (pos) Устанавливает текущую позицию чтения в значение pos
seekg (offs, org) Перемещает текущую позицию чтения на offs байтов, считая от одной из трех позиций, определяемых параметром org: ios : : beg (от начала), ios : : cur (от текущей позиции) и ios : : end (от конца файла)
tellg ( ) Возвращает текущую позицию чтения потока
Класс ostream
flush ( ) Записывает содержимое потока вывода на физическое устройство
рut (с) Выводит в поток символ с и возвращает ссылку на поток

Окончание таблицы

seekg (pos) Устанавливает текущую позицию записи в значение pos
seek (ofs, org) Перемещает текущую позицию записи на offs байтов, считая от одной из трех позиций (см. выше)
tellg ( ) Возвращает текущую позицию записи потока
write (buf, num) Записывает в поток num символов из массива buf и возвращает ссылку на поток

Пример 1. Программа считывает строки из входного потока в символьный массив.

# include <iostream.h>

int main ( ) {

const int N = 20, Len = 100;

char str [Len] [N]

int i = 0;

while (cin.getline (str [i], Len, ‘\n’) & & i < N ) {

i ++;}

return 0;

}

Пример 2. Программа формирует файл test, в который вводится три строки (файловые потоки см. далее).

# include <iostream.h>

# include <fstream.h>

# include <string.h>

int main ( ) {

// Запись в файл

ofstream out (“test”);

if (!out) {

cout << “Нельзя открыть файл для записи” << endl;

return 1;

}

char *str [ ] = {“the first line”, “the second line”, “the third line”};

for (int i = 0; i<3, ++ i) {

out.write (str [i], strlen (str [i]));

out.put (‘\n’);

}

out.close ( );

// Чтение из файла

ifstream in (“test”);

if (! in) {

cout << “Нельзя открыть файл для чтения” << endl;

return 1;

}

char check_str [3] [30];

for (int i = 0; i < 3; ++ i) {

in.get (check_str [i], 30);

in.get ( ); }

for (int i = 0; i < 3; ++ i ) cout << check_str [i] << endl;

in.close ( );

return 0;

}

Пример 3. Программа с функциями peek ( ) и putback ( ).

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

# include <iostream.h>

# include <fstream.h>

# include <stdlib>

int main ( ) {

char ch;

// Подготовка файла

ofstream out (“test”);

cout << “Нельзя открыть файл test для записи” << endl;

return1;

}

char str [80], *p;

out << 123 << “Это выполняется тест” << 23;

out << “Всем привет” << 99 << “Пока” << endl;

out.close ( );

// Чтение файла

ifstream in (“test”);

if (!in) {

cout << “Нельзя открыть файл test для чтения” << endl;

return 1;

}

do {

p = str;

ch = in.peek ( ); // определяем тип следующего значения

if (isdigit(ch)) {

while (isalpha (*p = in.get ( ))) p ++; // считываем целые

in.putback (*p); // возврат символа в поток

*р = “\0”; // заканчиваем строку нулем

cout << “Число:” << atoi (str);

}

else if (isalpha (ch)) { //считываем строку

while (isalpha (*p = in.get ( ))) p ++;

in.putback (*p); // возврат символа в поток

*p = ‘\0’; // заканчиваем строку нулем

cout << “Строка:” << str;

}

else in.get ( ); // пропуск

cout << endl;

} while (!in.eof ( ));

in.close ( );

return 0;

}

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

Число: 123

Строка: Это

Строка: выполняется

Строка: тест

Число: 23

Строка: Всем

Строка: привет

Число: 99

Строка: Пока

Ошибки потоков

В базовом классе ios определено поле state, которое представляет собой состояние потока в виде совокупности битов:

enum io_state {

googbit = 0x00, // Нет ошибок

eofbit = 0x01, // Достигнут конец файла

failbit = 0x02, // Ошибка форматирования или преобразования

badbit = 0x04, // Серьезная ошибка, после которой пользоваться потоком

// нельзя

hardbit = 0x08 // Неисправность оборудования

};

Состоянием потока можно управлять с помощью следующих методов и операций:

int rdstate ( ) – возвращает текущее состояние потока;

int eof ( ) – возвращает ненулевое значение, если установлен флаг eofbit;

int fail ( ) – возвращает ненулевое значение, если установлен один из флагов failbit, badbit или hardbit;

int good ( ) – возвращает ненулевое значение, если сброшены все флаги ошибок;

void clear (int = 0) – параметр принимается в качестве состояния ошибки, при отсутствии параметра состояние ошибки устанавливается 0;

operator void *( ) – возвращает нулевой указатель, если установлен хотя бы один бит ошибки;

operator ! ( ) – возвращает ненулевой указатель, если установлен хотя бы один бит ошибки.

Пример. Наиболее часто используемые операции с флагами состояния потока:

// Проверить, установлен ли флаг flag

if (steam_obj.rdstate ( ) & ios : : flag)

// Сбросить флаг flag

stream_obj.clear (rdstate ( ) & ~ios : : flag)

// Установить флаг flag

stream_obj.clear (rdstate ( ) | ios : : flag)

// Установить флаг flag и сбросить все остальные

stream_obj.clear (ios : : flag)

// Сбросить все флаги

stream_obj.clear ( )

 

Операция void *( ) неявно вызывается всякий раз, когда поток сравнивается с нулем. Это поволяет записывть циклы вида:

while (stream_obj) {

// Все в порядке, можно производить ввод/вывод

}

Файловые потоки

 

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

ifstream – класс входных файловых потоков;

ofstream – класс выходных файловых потоков;

fstream – класс двунаправленных файловых потоков.

Эти классы являются производными от классов istream, ostream и iostream соответственно.

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

- создание потока;

- открытие потока;

- обмен (ввод/вывод);

- уничтожение потока;

- закрытие файла.

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

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

ifstream ( );

ofstream ( );

fstream ( );

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

ifstream (const char * name, int mode = ios : : in);

ofstream (const char * name, int mode = ios : : out | ios : : trunc);

fstream (const char * name, int mode = ios : : in | ios : : out);

здесь второй параметр – это режим открытия файла.

 

 

Открыть файл в программе можно с использованием либо конструкторов, либо метода open, имеющего такие же параметры, как и в соответствующем конструкторе.

Пример 1

ifstream inpf (“input.txt”); // используем конструктор

if (! inpf) {

cout << “Невозможно открыть файл для чтения”;

return 1;

}

Пример 2

ofstream f;

f.open (“output.txt”, ios : : out); //используем метод open

if (!f) {

cout << “Невозможно открыть файл для записи”;

return 1;

}

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

Пример. Вывод на экран содержимого файла.

# include <fstream.h>

int main ( ) {

char text [81], buf [81];

cout << “Введите имя файла”;

cin >> text;

ifstream f (text);

if (!f) {

cout << “Ошибка открытия файла”;

return 1;

}

while (!f.eof ( )) {

f.getline (buf,81);

cout << buf << endl;

}

return 0;

}

Для закрытия потока определен метод close ( ), но поскольку он неявно выполняется деструктором, явный вызов необходим только тогда, когда требуется закрыть поток раньше конца его области видимости.

Задание

Перепишите вашу программу – телефонный справочник с использованием файловых потоков и форматированного ввода-вывода.

Строковые потоки

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

istringstream – входные строковые потоки;

ostringstream – выходные строковые потоки;

stringstream – двунаправленные строковые потоки.

Эти классы определены в заголовочном файле <sstream> и являются производными от классов istream, ostream и iostream соответственно.

Участки памяти, с которыми выполняются операции чтения и извлечения, по стандарту определяются как строки С++ (класс string). Строковый класс будет рассмотрен далее. Строковые потоки создаются и связываются с этими участками памяти с помощью конструкторов:

explicit istringstream (int mode = ios : : in);

explicit istringstream (const string & name, int mode = ios : : in);

explicit ostringstream (int mode = ios : : out);

explicit ostringstream (const string & name, int mode = ios : : out);

explicit stringstream (int mode = ios : : in | ios : : out);

explicit stringstream (const string & name, int mode = ios : : in | ios : : out);

Здесь слово explicit указывает на то, что эти конструкторы вызываются только явным образом.

В строковых потоках описан метод str, возвращаюший копию строки или устанавливающий ее значение:

string str ( ) const;

void str (const string & S);

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

# include <sstream>

# include <string>

# include <iostream>

# include <ctime>

using namespace std;

string message (int i) {

ostringstream os;

time_t t;

time (&t); // Функция возвращает текущие дату и время в виде time_t

os << “time: “ << ctime (&t) <<”number: ” << i << endl;

return os.str ( ); // Функция ctime () превращает время

// в строку

}

int main ( ) {

cout << message (22);

return 0;

}


 

Строки

 

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

Тип данных (класс) string стандартной библиотеки лишен недостатков, присущих массивам символов. Основные действия со строками выполняются в нем с помощью операций и методов, а длина строки изменяется динамически. Для использования класса необходимо подключать к программе заголовочный файл <string>.