Флаги и форматирующие методы
ВВЕДЕНИЕ
Стандартную библиотеку С++ можно разделить на две части. К первой части относятся функции, макросы, типы и константы, унаследованные из библиотеки С; ко второй – классы и другие средства С++.
Классы стандартной библиотеки можно разделить на следующие группы.
Потоковые классы – предназначены для управления потоками данных между оперативной памятью и внешними устройствами, а также в пределах оперативной памяти.
Строковый класс – предназначены для работы с символьными переменными.
Контейнерные классы – реализуют структуры для хранения данных (списки, векторы, множества).
Итераторы – предназначены для унифицированного доступа к элементам классов.
Математические классы – поддерживают обработку массивов с плавающей точкой и работу с комплексными числами.
Диагностические классы – обеспечивают динамическую идентификацию типов и объектно-ориентированную обработку ошибок.
Остальные классы обеспечивают динамическое распределение памяти и ряд других функций.
Для использования средств стандартной библиотеки в программу следует включить соответствующие заголовочные файлы.
Потоковые классы
Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.
Чтение данных из потока извлечением, вывод в поток – помещением или включением. Поток определяется как последовательность байтов и не зависит от конкретного устройства, с которым производится обмен. Обмен с потоком для увеличения скорости передачи данных производится, как правило, через специальную область данных – буфер.
По направлению обмена потоки можно разделить на входные (данные вводятся в память) и двунаправленные (допускающие как извлечение, так и включение).
По виду устройств, с которыми работает поток, можно выделить стандартные, файловые и строковые потоки.
Стандартные потоки предназначены для передачи данных от клавиатуры и на экран.
Файловые потоки – для обмена информацией с файлами на внешних устройствах.
Строковые потоки – для работы с массивами символов.
Для поддержки потоков библиотека С++ содержит иерархию классов, построенную на основе двух базовых классов – 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>.