Вывод int из потока в c. Потоковые классы
02.08.2009 20:46 |
Предисловие
Стандартные потоки ввода/вывода
Поток вывода cout
выведет abc . |
void f(int i) { std::cout } |
Поток ввода cin
Для считывания информации, вводимой пользователем с клавиатуры, служит стандартный поток cin . Как и поток вывода, поток ввода работает с символьным представлением типов. То, как будут интерпретироваться вводимые символы, зависит от второго аргумента оператора >> (того, что справа). Соотетственно, вам не нужно указывать, какого типа данные предполагается считать, компилятор определит это исходя из типа переменной. Посмотрим в качестве примера программу, реализующую наипростейший калькулятор:
//Пример 5.1 #include Int main() Std::cout
std::cin >> a; |
Выведя первое сообщение, программа ожидает, пока в потоке ввода не окажется что-нибудь, что можно будет считать в переменную типа int . Как только мы введем что-то и нажмем клавишу Enter, в поток передастся введенная нами строка. Если введенное удастся интерпретировать как символьное представление целого числа, программа благополучно запишет это число в переменную и продолжит свое выполнение. Если же была введена какая-нибудь не относящаяся к делу белиберда, то программа просто будет грязно ругаться по-английски (в зависимости от языка вашей реализации C++). Последнее, конечно, не удивительно, т.к. программа ожидает от программиста и пользователя разумного поведения - либо пользователь должен вводить все в точности как надо, либо программист должен предусмотреть "защиту от дурака".
Вернемся, однако, к нашим потокам. Итак, получив возможность считать в целую переменную соответствующее значение, программа ее таки считывает и выводит следующее сообщение, ожидая, что теперь ей введут один-единственный символ. Предположим, что пользователь действительно вводит один из символов +, -, * или / и вновь нажимает Enter. Тогда программа вновь успешно считывает этот символ в переменную sign и продолжает свою работу. Что будет дальше, вы должны уже смочь представить самостоятельно.
Следующее, что важно сказать, это то, что при считывании в переменные встроенных типов оператор >> считает концом ввода первый же встретившийся символ-разделитель
. К таким символам относятся, например, пробел и символ перевода строки ("\n" , вводится нажатием клавиши Enter). Чтобы стало понятнее, что происходит, рассмотрим с этой позиции предыдущую программу.
Во-первых, ее код можно было бы переписать примерно следующим образом:
//Пример 5.2 #include Int main() Std::cout
std::cin >> a >> sign >> b; |
Если бы пользователь ввел (обязательно с пробелами!) строку 12 + 34 , то произошло бы следующее: после того, как была введена строка и нажата клавиша Enter, в поток ввода cin была бы передана строка 12 + 34 . Ожидающая момента, когда этот поток станет непустым, инструкция std::cin >> a считала бы фрагмент от начала строки до первого символ-разделителя. Т.е. было бы считано "слово" 12 , переведено в числовую форму и присвоено переменной a . Затем настал бы черед второго "слова" - того, что находилось в строке перед следующим пробелом. Символ + после этого оказывается считан, наступает черед "слова" 34 . Считав и его, программа завершает ввод, т.к. больше ей ничего не нужно узнавать, а если в потоке остались еще какие-то "лишние" "слова", то они окажутся просто невостребованы.
Итак, что мы видим? При вводе строки и нажатии клавиши Enter происходит запись данных в поток ввода
. После этого ожидающие непустого потока инструкции начинают считывать из потока
. Поэтому то, что вы нажали Enter, не значит, что все введенное считается единой строкой. Напротив, считываться будут отдельные "слова", и это можно использовать для ввода сразу нескольких переменных - нужно только разделить их пробелом.
Если же вам нужно считать целую строку, воспользуйтесь функцией getline:
void h() { std::string s; getline(std::cin, s); } |
Продолжим разбор примера 5.1. Что же у нас "во-вторых"? А во-вторых у нас то, что если бы пользователь после того, как программа вывела Введите первое число, ввел бы 12 + 34 , то программа бы считала "слово" 12 , перевела бы его в числовую форму, присвоила значение переменной a ; затем бы вывела предложение ввести знак и... тут же бы его считала, поскольку в потоке уже есть
данные для последующего считывания. Считав символ, она бы вывела строку Введите второе число, после чего считала бы "слово" 34 , не утруждая больше пользователя необходимостью прикасаться к клавиатуре. После чего бы все посчитала и вывела бы результат - строковые представления первого числа, знака, второго числа, знака равенства и числа-ответа, что выглядело бы как 12+34=46 .
Внешне бы это выглядело бы довольно странно: программа дважды просит что-то ввести, но ничего не считывает. Однако, на самом деле ей и не нужно больше ничего считывать - в потоке ввода
уже есть все нужные программе данные. Если, конечно, пользователь не ввел то что нужно, а не какую-нибудь ерунду.
Словом, тут мы видим очередной пример того, что компьютер делает то, что ему сказано, а вовсе не обязательно то, что от него хотят. Поэтому важно понимать механику всего происходящего, чтобы эффективно нагружать компьютер работой.
Небольшое техническое замечание
Может создаться впечатление, что для ввода/вывода данных мы используем потоки подобно функциям, как например printf и scanf: вызываем его, указываем куда/откуда считывать данные, и получаем результат. На самом деле это не совсем так. Стандартные потоки, такие как cin и cout , являются классами, т.е. типами, определяемыми пользователем (в данном случае - создателями стандартной библиотеки). К механизмам взаимодействия с клавиатурой и монитором они подключаются совершенно независимо от нас, можно считать, что они есть независимо от указания в нашем коде строк наподобие cout > , определенные для классов потоков - они принимают в качестве аргументов конкретный поток и переменную, и записывают данные из одного в другое. Поток же является совокупностью хранимой в нем информации и способов работы с этой информации, в частности, операторов > .
О семействе функций printf в следующем уроке.
Стандартные библиотеки C ++ предоставляют расширенный набор возможностей ввода / вывода, которые мы увидим в последующих главах. В этой главе будут рассмотрены основные и наиболее распространенные операции ввода-вывода, необходимые для программирования на C ++.
C ++ I / O происходит в потоках, которые представляют собой последовательности байтов. Если байты поступают с устройства, такого как клавиатура, дисковод или сетевое соединение и т. д. В основную память, это называется операцией ввода, и если байты поступают из основной памяти в устройство, такое как экран дисплея, принтер, дисковод, или сетевое соединение и т. д., это называется операцией вывода .
Файлы заголовков библиотеки ввода-вывода
Для программ на C ++ важны следующие файлы заголовков -
Этот файл определяет объекты cin, cout, cerr и clog , которые соответствуют стандартным входным потокам, стандартным потокам вывода, потоку стандартной буферизации без буферизации и потоку стандартной буферизации, соответственно.
Этот файл объявляет услуги, полезные для выполнения отформатированного ввода / вывода с помощью так называемым параметризованным потоком манипуляторами, такие как setw и setprecision .
Этот файл объявляет службы для обработки файлов, управляемых пользователем. Мы обсудим это подробно в главе «Файл и поток».
Стандартный выходной поток (cout)
Предопределенный объект cout является экземпляром класса ostream . Сообщается, что объект cout «подключен к» стандартным устройствам вывода, который обычно является экраном дисплея. СоиЬ используется в сочетании с оператором вставки потока, который записывается в виде << которые являются два меньше, чем знаки, как показано в следующем примере.
#include
Value of str is: Hello C++
Компилятор C ++ также определяет тип данных переменной, подлежащей выводу, и выбирает соответствующий оператор вставки потока для отображения значения. Оператор << перегружен для вывода элементов данных встроенных типов integer, float, double, string и значений указателя.
Оператор ввода << может использоваться более одного раза в одном выражении, как показано выше, и endl используется для добавления новой строки в конце строки.
Стандартный входной поток (cin)
Предопределенный объект cin является экземпляром класса istream . Говорят, что объект cin прикреплен к стандартным устройствам ввода, которые обычно являются клавиатурой. CIN используется в сочетании с оператором экстракции потока, который записывается как >> , которые являются два больше, чем знаки, как показано в следующем примере.
#include
Когда вышеуказанный код скомпилирован и выполнен, он предложит вам ввести имя. Вы вводите значение, а затем нажмите Enter, чтобы увидеть следующий результат -
Please enter your name: cplusplus Your name is: cplusplus
Компилятор C ++ также определяет тип данных введенного значения и выбирает соответствующий оператор извлечения потока для извлечения значения и сохранения его в данных переменных.
Оператор извлечения потока >> может использоваться более одного раза в одном выражении. Чтобы запросить более одного элемента данных, вы можете использовать следующее:
Cin >> name >> age;
Это будет эквивалентно следующим двум утверждениям:
Cin >> name; cin >> age;
Стандартный поток ошибок (cerr)
Предопределенный объект cerr является экземпляром класса ostream . Говорят, что объект cerr прикреплен к стандартным устройству ошибок, которое также является экраном дисплея, но объект cerr не забуферирован, и каждая вставка потока в cerr вызывает немедленный вывод его вывода.
Сегг
#include
Когда приведенный выше код компилируется и выполняется, он производит следующий результат:
Стандартный поток журнала (засорение)
Предопределенный объект clog является экземпляром класса ostream . Объект clog, как говорят, прикреплен к стандартному устройству ошибок, который также является экраном дисплея, но затвор объекта буферизуется. Это означает, что каждая вставка для блокировки может привести к тому, что ее вывод будет удерживаться в буфере до заполнения буфера или до тех пор, пока буфер не будет сброшен.
Засорить также используется в сочетании с оператором вставки потока, как показано в следующем примере.
#include
Когда приведенный выше код компилируется и выполняется, он производит следующий результат:
Error message: Unable to read....
Вы не сможете увидеть какие-либо различия в cout, cerr и clog с этими небольшими примерами, но при написании и выполнении больших программ разница становится очевидной. Поэтому хорошей практикой является отображение сообщений об ошибках с использованием потока cerr и при отображении других сообщений журнала, которые следует использовать.
файла, устройства или записывания на него этих данных;- Стандартный поток ввода (обозначение: stdin , cin и др.) - используется для ввода символьных данных в программу. По-умолчанию этот поток закреплён за клавиатурой компьютера;
- Стандартный поток вывода (обозначается как: stdout , cout и др.) - используется для вывода символьной информации, полученной в результате работы программы в "штатном режиме". По-умолчанию этот поток закреплён за экраном дисплея;
- Стандартный поток ошибок (обозначение: stderr
, cerr
и др.) - используется для вывода символьных диагностических сообщений
, ошибок и предупреждений, возникших в результате работы программы. По-умолчанию этот поток закреплён за экраном дисплея;
Примечание: стандартный поток и поток ошибок разделены в связи с тем, что при перенаправлении вывода часто совсем не нужно записывать в результаты работы программы диагностические сообщения. Эти сообщения будут лишними, например, при формировании таблицы базы данных в виде текстового файла;
- Стандартный поток печати (обозначение: stdprn и др.) - используется для вывода результатов работы программы на печать. По-умолчанию этот поток закреплён за текущим принтером в системе, подключённым к порту LPT1 . В настоящее время этот поток почти не используется, поскольку чаще проще и безопаснее перенаправить стандартный поток вывода на принтер, чем разделять потоки отдельно для экрана и отдельно для принтера.
Все остальные потоки создаются или уничтожаются с помощью функций открытия и закрытия файлов, на период чтения/записи/добавления информации в эти файлы.
6.2. Ввод со стандартного потока ввода
6.2.1. Ввод средствами языка Си
Ввод со стандартного потока в Си осуществляется при помощи следующих функций:
- для ввода одиночного символа - функцию getchar ;
- для ввода строки символов без ограничения на длину - функцию gets ;
- для "форматированного ввода" символов и их преобразования в "двоичные значения" переменных - функцию scanf ;
С синтаксисом и правилами использования этих функций можно ознакомиться в приложении №I к данной лекции. Автор хочет отметить, что использование функций ввода со стандартного потока является небезопасным, а, следовательно, и нежелательным способом ввода данных из стандартного потока. По возможности, заменяйте эти функции функциями потокового чтения данных с "явным" указанием потоков и "длины" прочтённой строки.
Пример 6.1
/* Файл ex06001.c */
/* Функция иллюстрирует потоковый ввод-вывод (как это делать нельзя)*/
#include
Например, функция, приведённая в примере 6.1, при вводе строки: "aaaaaaaa\n" , - вызовет аварийное завершение с ошибкой "переполнение буфера". Поскольку длина текстового буфера - 2 символа (последний символ - "нулевой", знак окончания строки "\0" ), то лишние символы, введённые с клавиатуры, попадают в "запредельную" системную область данных. В этом случае происходит операция (прерывание) "отказ системы", и адрес этой системной области оказывается доступной "взломщику системы". Функции, приведённые в примерах 6.2 и 6.3, лишены этих недостатков;
Пример 6.2
/* Файл ex06002.c */
/* Функция иллюстрирует потоковый ввод-вывод (как это делать надо)*/
/* Пример тестировался в системе программировани
Borland C/C++ 3.10 */
#include
Пример 6.3
/* Файл ex06003.c */
/* Функция иллюстрирует потоковый ввод-вывод (как это делать надо)*/
/* Пример тестировался в системе программировани
Borland C/C++ 3.10 */
#include
6.2.2. Ввод средствами языка C++
Консольный потоковый ввод в языке C++ осуществляется при помощи операторов ">>" , а также при помощи функций get , peek и ignore . Стандартный поток ввода в языке C++ обозначается как cin (см. таблицу 6.1).
Обозначения потоков | |||||
---|---|---|---|---|---|
Поток | Ввод (стандартный) | Вывод (стандартный) | Вывод ошибок | Передача на линию | Печать |
Значение дескриптора | 0 | 1 | 2 | 3 | 4 |
Обозначение в Си | stdin | stdout | stderr | stdaux | stdprn |
Обозначение в C++ | cin | cout | cerr | нет | нет |
Обозначение в Java | System.in | System.out | ? | нет | нет |
Обозначение в Perl | STDIN | STDOUT | STDERR | нет | нет |
Обозначение в Python | sys.stdin | sys.stdout | sys.stderr | нет | нет |
Обозначение в VBScript | StdIn | StdOut | StdErr | нет | нет |
Описание функций для потокового ввода средствами языка C++ смотри в приложении №III к данной лекции.
C помощью оператора ">>" можно осуществить как неформатированный, так и форматированный ввод-вывод. Однако автор советует для ввода использовать неформатированный ввод из потока с помощью функции cin.get , а затем "выделить и ввести" нужные двоичные значения при помощи функции sscanf (см. пример 6.4.).
Пример 06.004 .
/* File ex06004.cpp */
/* Пример ввода-вывода средствами C++*/
/* Пример тестировался в системе программировани
Borland C/C++ 3.10 */
#include
В этом примере для ввода символьной строки использовалась функция cin.get , для вывода - оператор "<<" , а для форматных преобразований - функции sprintf и sscanf .
Описание функций sscanf и sprintf см. в приложении №I к данной лекции.
Замечание: из личного опыта автора родился следующий совет: при работе с консолью, когда не требуется программирование фильтров и перенаправления потоков , лучше использовать консольные функции ввода-вывода. Для программирования фильтров, наоборот, требуется ввод-вывод исключительно файловыми потоковыми функциями. В обоих случаях для форматного преобразования необходимо использовать только функции sprintf и sscanf , предварительно проверив (т.н. "валидация") формат читаемых и записываемых символов. Во всех случаях нужно жёстко контролировать итоговую длину строки ввода-вывода.
До сих пор в программных примерах мы пользовались только функциями стандартной библиотеки С. Однако в C++ имеются собственные средства, основанные на принципах классовой модели. Другими словами, в исполнительной библиотеке C++ имеется набор классов для управления вводом-выводом.
В отличие от функций буферизованного ввода-вывода С (таких, как print f и scanf, не выполняющих никаких проверок на соответствие аргументов форматной строке) классы потоков C++ безопасны в отношении типа. Ввод-вывод использует механизм перегрузки операций, гарантирующий вызов нужной функции-операции для указанного типа данных. Это главное преимущество потоков языка C++.
Классы потоков
К классам потоков относятся следующие:
- Класс streambuf управляет буфером потока, обеспечивая базовые операции заполнения, опорожнения, сброса и прочих манипуляций с буфером.
- Класс ios является базовым классом потоков ввода-вывода.
- Классы istream и ostream — производные от ios и обеспечивают работу потоков соответственно ввода и вывода.
- Класс iоstream является производным от двух предыдущих и предусматривает функции как для ввода, так и для вывода.
- Классы ifstream, of stream и f stream предназначены для управления файловым вводом-выводом.
- Классы istrstream и ostrstream управляют резидентными потоками (форматированием строк в памяти). Это устаревшая методика, оставшаяся в C++Builder в качестве пережитка.
Для работы с потоками вам потребуется включить в программу заголовочный файл iostream.h. Кроме того, может потребоваться подключить файлы fstream.h (файловый ввод-вывод), iomanip.h (параметризованные манипуляторы) и strstream.h (форматирование ь памяти).
Предопределенные потоки
Библиотека ввода-вывода C++ предусматривает четыре предопределенных объекта-потока, связанных со стандартными входным и выходным устройствами. Ниже дана сводка этих объектов.
Таблица 9.1. Предопределенные объекты-потоки C++
Имя | Класс | Описание |
istream | Ассоциируется со стандартным вводом (клавиатурой). |
|
cout | ostream | Ассоциируется со стандартным выводом (экраном). |
cerr | ostream | Ассоциируется со стандартным устройством ошибок (экраном) без буферизации. |
clog | ostream | Ассоциируется со стандартным устройством ошибок (экраном)с буферизацией. |
Операции извлечения и передачи в поток
Основными классами ввода-вывода C++ являются istream и ostream. Первый из них перегружает операцию правого сдвига (>>), которая служит в нем для ввода данных и называется операцией извлечения из потока. Класс ostream перегружает соответственно операцию левого сдвига (<<); она применяется для вывода и называется операцией передачи в поток.
Примечание
Нужно сказать, что стандартной русской терминологии как таковой в C++ не существует. Каждый изобретает свою собственную; иногда удачно, иногда — нет.
Вот простейшие операторы ввода и. вывода на стандартных потоках:
#include
int main()
char name [ 8.0] ;
cout<< "Enter your name: ";
cin>> name;
cout <<"Hello " << name << "!";
return 0;
Как видите, при действиях над потоками возможно последовательное
сцепление операций, подобно последовательному присваиванию. Как вы уже знаете, такая форма записи обеспечивается благодаря тому, что функции-операции извлечения и передачи возвращают ссылку на свой объект.Перегруженные операции для встроенных типов
Классы istream и ostream перегружают операции соответственно извлечения и передачи в поток для всех встроенных типов. Это позволяет единообразно применять эти операции для чтения и вывода символов, целых, вещественных чисел (т. е. с плавающей точкой) и строк. Вот небольшая иллюстрация, где попутно показан еще простейший прием проверки на ошибку при вводе:
#include
void check(void) {
if (!cin.good())
// Либо просто if (!cin) {
cout << "Error detected!";
exit (1);
int main(void)
double d;
long 1;
cout << "Enter a floating point value: ";
cin >> d;
check () ;
cout << "You entered: " << d << "n";
cout << "Enter an integer value: ";
cin >> 1;
check () ;
cout << "You entered: " << 1 << "n";
return 0;
Примечание
Операции извлечения и передачи в поток (соответственно для классов istream и ostream) можно перегрузить таким образом, чтобы можно было применять их для ввода или вывода объектов класса, определенного пользователем. Приведенный ниже пример демонстрирует эту методику. Вообще-то в подобных случаях совершенно необходимо предусмотреть детектирование и обработку ошибок ввода, но здесь мы этого не сделали.
#include
class Point { int x, у;
public:
Point(int xx = 0, int yy = 0) {
X = xx; у = yy;
Friend istream &operator>>(istream&, Points);
friend ostream &operator“(ostream&, Points);
istream &operator”(istream &is, Point &p)
// При вводе точка представляется просто парой чисел,
// разделенных пробелом.
// is >> р.х > р.у;
return is;
ostream &operator<<(ostream &os.Point &p) {
// Вывод в виде (х, у).
os<< " ("<< р. х<< ", "<< р. у<<") " ;
return os;
int main() {
Point р;
cout<< "Enter point coordinates: ";
cin>> р;
cout<< "The point values are " << р;
return 0;
Форматирование
Библиотека ввода-вывода предусматривает три способа форматирования: посредством вызова
форматирующих функций-элементов, с помощью манипуляторов или путем установки или сброса флагов потока.Форматирующие функции-элементы
Эти функции являются элементами класса ios и перегружены таким образом, чтобы можно было либо читать, либо устанавливать значение соответствующего атрибута потока. Если аргумент в вызове отсутствует, функция возвращает текущее значение атрибута. Если аргумент указан, функция устанавливает новое и возвращает предыдущее значение атрибута.
long width(long)
Эта функция предназначена для чтения или установки атрибута ширины поля.
- Применяемая ко входному потоку, функция позволяет задать максимальное число вводимых символов.
- На выходном потоке функция задает минимальную ширину поля вывода.
- Если действительное поле вывода меньше установленной ширины, выводятся дополнительные заполняющие символы. Символ заполнения определяется специальным атрибутом потока.
- Если действительное поле вывода больше установленной ширины, ее значение игнорируется.
- Значением ширины по умолчанию является 0 (ширина поля определяется выводимыми данными).
- Ширина поля сбрасывается в 0 после каждой передачи в поток.
char fill(char)
По умолчанию символ заполнения — пробел.
long precision(long)
- Точность по умолчанию равна шести цифрам.
- Если установлен флаг scientific или fixed, точность задает число цифр после десятичной точки.
- Если ни один из этих флагов не установлен, точность задает общее число значащих цифр.
Пример
Ниже приводится программа, демонстрирующая форматирование потока с помощью функций-элементов класса ios.
Листинг 9.1. Демонстрация форматирующих функций потока
///////////////////////////////////////////////
// Format.срр: Форматирующие функции-элементы ios.
#include
#pragma hdrstop
#include
#pragma argsused
// Ширина поля при вводе и выводе.
cnar sir ;
cout<< "Enter something: ";
cin.width(16); // Ввод не более 15 символов. cin>> str;
cout.width(32); // Вывести в поле шириной 32. cout << str<< "nn";
// Заполняющий символ и ширина поля. Ширина сбрасывается
// после каждой операции, поэтому она устанавливается
// для каждого числа.
int h = 7, m = 9, s = 0; // Выводятся в виде hh:mm:ss.
cout.fill("0"); cout << "Time is ";
cout.width (2); cout << h << " : " ; cout.width (2) ;
cout<< m<< " : " ;
cout.width (2) ;
cout<< s<< ".nn";
cout.fill (" "); // Восстановить пробел.
// Точность.
double d = 3.14159265358979;
float f = 27182.81828;
cout.precision (5);
cout << f << "n"; . // Выводит "27183" .
cout << d << "n"; " // Выводит "3.1416".
cout .precision (4) ;
cout << f << "n"; // Выводит "2.718е+04".
cout.setf(ios::fixed); // Установить флаг fixed.
cout<< f<<"n"; // Выводит "27182.8184".
return 0;
Манипуляторы
Манипуляторы потоков являются по существу функциями, которые можно вызывать непосредственно в цепочке операций извлечения или передачи в поток. Различают простые и параметризованные манипуляторы. У простых манипуляторов аргументы отсутствуют. Параметризованные манипуляторы имеют аргумент.
Ниже приводится сводка имеющихся манипуляторов, как простых, так и параметризованных. Они Перечислены в алфавитном порядке.
Таблица 9.2. Простые и параметризованные манипуляторы
Манипулятор | Описание |
Задает десятичную базу преобразования. |
|
end1 | Передает в поток символ новой строки и сбрасывает поток. |
ends | Передает в поток символ завершающего строку нуля. |
flush | Сбрасывает выходной поток. |
Задает шестнадцатеричную базу преобразования. |
|
lock(ios Sir) | Блокирует дескриптор файла потока ir. |
Задает восьмеричную базу преобразования. |
|
resetiosflags(int f) | Сбрасывает флаги, биты которых установлены в f. |
setbase(int b) | Устанавливает базу преобразования (0, 8, 10 или 16). |
setiosflags(int f) | Устанавливает флаги, биты которых установлены в f. |
setfill(int c) | Задает символ заполнения (аналогичен функции fiilO). |
setprecision(long p) | Задает точность (аналогичен функции precision ()). |
setw(iong w) | Задает ширину поля (аналогичен функции width ()). |
lunlock(ios &ir) | Разблокирует дескриптор файла для потока ir. |
Исключает начальные пробельные символы. |
Вот пример использования некоторых манипуляторов (мы создали один свой собственный):
Листинг 9.2. Форматирование с помощью манипуляторов
/////////////////////////////////////////////////
// Manip.cpp: Демонстрация некоторых манипуляторов.
#include
#pragma hdrstop
#include
//////////////////////////////////////////////////
// Манипулятор, определенный пользователем - звонок.
ostream shell(ostream &os)
return os<< "a";
#pragma argsused
int main(int argc, char* argv)
cout “ bell; // Тестирование манипулятора bell.
// Манипуляторы базы преобразования.
long 1 = 123456;
cout<< "Hex: "<< hex<< 1<< end1
<<"Oct: "<< oct<< 1<< end1
<< "Dec: " << dec << 1 << end1;
// Параметризованные манипуляторы.
int h=12, m=5, s=0; // To же, что в примере
// Format.cpp. cout << "The time is " << setfill("0")
<< setw(2) << h << ":"
<< setw(2) << m << ":"
<< setw(2) << s << setfillC ") << end1;
return 0;
Как видите, очень несложно определить свой собственный
простой манипулятор. Это всего лишь функция, возвращающая ссылку на переданный ей в параметре поток.Примечание
Создать параметризованный манипулятор не так просто. Существуют различные способы сделать это, но наиболее очевидный из них — реализация манипулятора через класс эффектора. Идея состоит вот в чем. Нужно определить для манипулятора собственный класс с конструктором, принимающим нужные параметры, .и перегрузить для этого класса операцию передачи (извлечения) соответствующего потока. После этого конструктор можно вызывать в качестве параметризованного манипулятора. Создается временный объект, который выводится в поток перегруженной операцией и удаляется. Ниже показан манипулятор, который выводит в поток свой аргумент типа unsigned в двоичной форме.
#include
// Класс эффектора.
class Bin {
int val;
public:
Bin(unsigned arg) { val = arg; }
friend ostream &operator“(ostreams. Bin);
// Вывод числа в двоичной форме.
ostream &ooerator<<(ostream &os. Bin b) {
int cb = 1; // Контрольный бит для отсчета циклов.
do {
if (b.val <0) // Если val < 0, то старший бит = 1. os << 1;
else
os<< 0;
} while (b.vai<<= 1, cb<<= 1) ;
return os;
int main ()
unsigned n = Ox00ff0f34;
cout<< "Some binary: "<< Bin(n)<< end1;
return 0;
Рис. 9.1 Манипулятор, выводящий свой аргумент в двоичной форме
Форматирующие флаги
Флаги управления форматированием являются битовыми полями, хранящимися в переменной типа fmtflags (псевдоним int). Для их чтения и/или модификации могут применяться следующие функции-элементы класса ics:
- int flags (), int flags (int). Без параметра возвращает текущее состояние флагов. При указанном параметре устанавливает новые значения флагов и возвращает их прежнее состояние.
- int setf(int), long setf(int, int). Первая форма устанавливает флаги, биты которых установлены в параметре. Вторая форма модифицирует флаги, биты которых установлены во втором параметре. Значения этих флагов задаются первым параметром. Возвращает прежнее состояние всех флагов.
- void unsetf(int). Сбрасывает флаги, биты которых установлены в параметре.
Помимо функций, для управления флагами можно пользоваться манипуляторами setiosflags (аналог setf() с одним параметром) и reset-iosflags (аналог unsetf ()).
В таблице 9.3 описаны форматирующие флаги потоков.
Таблица 9.3. Форматирующие флаги класса ios
Флаг | Описание |
|
internal | Если установлен, при выводе чисел знак выводится на левом краю поля вывода, а само число выравнивается по правому краю поля. Промежуток заполняется текущим символом заполнения. |
|
Устанавливает десятичное представление чисел. Принимается по умолчанию. |
||
Устанавливает восьмеричное представление чисел. |
||
Устанавливает шестнадцатеричное представление чисел. |
||
showbase | Если установлен, то при восьмеричном и шестнадцатеричном представлении чисел выводит индикатор основания (0 для восьмеричных и Ох для шестнадцатеричных чисел). |
|
showpoint | Если установлен, для вещественных чисел всегда выводится десятичная точка. |
|
uppercase | Если установлен, шестнадцатеричные цифры от А до F, а также символ экспоненты Е выводятся в верхнем регистре. |
|
boolalpfa | Если установлен, булевы значения выводятся как слова “true/false”. В противном случае они представляются соответственно единицей и нулем. |
|
showpos | Выводит + для положительных чисел. |
|
scientific | Если установлен, вещественные числа выводятся в научной (экспоненциальной) нотации. |
|
fixed | Если установлен, вещественные числа выводятся в десятичном формате (с фиксированной точкой). |
|
unitbuf | Если установлен, поток сбрасывается после каждой операции передачи. |
Несколько замечаний относительно перечисленных в таблице флагов.
- Флаги left, right и internal являются взаимоисключающими. В данный момент времени может быть установлен только один из них.
- Взаимоисключающими являются также флаги dec, oct и hex.
- При модификации базы представления в качестве второго параметра setf() можно использовать константу ios: :basefield.
- При модификации выравнивания в поле можно аналогичным образом использовать константу ios: :adjustfield.
- При модификации формы представления (нотации) чисел с плавающей точкой можно использовать константу ios: : floatfield. Ниже мы приводим листинг программы, демонстрирующей применение различных флагов форматирования.
Примечание
Имена перечисленных выше флагов и других констант принадлежат к области действия класса ios. Вне этого класса нужно либо воспользоваться разрешением области действия (ios: : scientific), либо обращаться к ним, как к элементам существующего объекта (cout. scientific). Мы поедпочитаем первый способ.
Листинг 9.3. форматирующие флаги потоков
////////////////////////////////////////////////////
// Flags.срр: Форматирующие флаги потоков.
#include
#include
#pragma hdrstop #include
#pragma argsused
int main(int argc, char* argv)
// Демонстрация флага skipws. Если его сбросить, то при
// наличии начальных пробелов при вводе возникает ошибка.
long 1;
cout<< "Enter an integer: ";
cin.unsetf(ios::skipws);
cin >> 1;
if (cin) // При ошибке потока
cin == NULL. cout<< "You entered "<< 1<< endl;
else {
cout << "Incorrect input."<< endl;
cin .clear (); // Обнуление битов ошибки.
} cout< // Демонстрация флагов основания и знака.
// Задается основание 16, вывод индикатора и знака +.
1 = 8191;
cout.setf(ios::hex, ios::basefield);
cout.setf(ios::showbase | ios::showpos);
<< " dec: " << 1 << endl;
cout << endl;
// Демонстрация флагов формата вещественных чисел.
double dl = 1.0е9, d2 = 34567.0;
cout <<"Default: " << dl << " "< // Вывод десятичной точки. cout.setf(ios::showpoint);
cout << "Decimal: " << dl<< " " << d2 << endl;
// Нотация с фиксированной точкой.
// Заодно сбросим вывод знака +.
cout.setf(ios
::
fixed, ios::floatfield | ios
::
showpos);
cout << "Fixed: " << dl << " " << d2 << endl;
cout<< endl;
// Вывод булевых значений как "true/false".
bool b = true;
cout.setf(ios::boolalpha) ;
cout << "Boolean values:
" << b << "" << !b
“
endl;
return 0;
Рис. 9.2
Демонстрация флагов форматиоования потока
Состояние потока
Состояние объекта класса ios (и производных от него) содержится в его закрытом элементе _state в виде набора битов. Следующая таблица перечисляет имеющиеся биты состояния потока.
Таблица 9.4. Биты состояния потока
Бит
Описание
goodbit
С потоком все в порядке (на самом деле это не какой-то бит, а 0 — отсутствие битов ошибки).
eofbit
Показывает, что достигнут конец файла.
failbit
Индицирует ошибку формата или преобразования. После очистки данного бита работа с потоком может быть продолжена.
badbit
Индицирует серьезную ошибку потока, связанную обычно с буферными операциями или аппаратурой. Скорее всего, поток далее использовать невозможно.
Для опроса или изменения состояния потока в классе ios имеется ряд функций и операций.
Примечание
Функция operator void*() неявно вызывается, если поток сравнивается с нулем (как cin в примере из листинга),
Файловые потоки
Файловые потоки библиотеки ввода-вывода реализуют объектно-ориентированную методику работы с дисковыми файлами. Имеется три класса таких потоков:
Эти классы выводятся соответственно из istream, ostream и iostream. Таким образом, они наследуют все их функциональные возможности (перегруженные операции << и>>” для встроенных типов, флаги форматирования и состояния, манипуляторы и т. д.).
Чтобы работать с файловым потоком, нужен, во-первых, объект потока, а во-вторых, открытый файл, связанный с этим объектом.
Конструирование объекта потока
Каждый из трех классов файловых потоков имеет четыре конструктора.
ifstream () ;
of stream();
fstream () ;
if stream(const char *name,
int mode = ios::in, long prot = 0666);
ofstream(const char *name,
int mode = ios::out, long prot = 0666);
fstream (const char *name, int mode, long prot = 0666);
ifstreamfint file);
ofstream(int file);
fstream (int file) ;
ifstream(int file, char *buf, int len)
of stream(int file, char *buf, int len)
fstream (int file, char *buf, int len)
Режимы открытия файла
Параметр mode, который имеет вторая форма конструктора, задает режим открытия файла. Для значений параметра класс ios определяет символические константы, перечисленные в таблице 9.5.
Таблица 9.5. Константы класса ios для режимов открытия файла
Константа
Описание
арр
Открытие для записи в конец файла.
При открытии позиционирует указатель на конец файла.
binary
Файл открывается в двоичном (не текстовом) режиме.
Файл открывается для ввода.
Файл открывается для вывода.
trunc
Если файл существует, его содержимое теряется.
Константы можно комбинировать с помощью поразрядного OR. Для конструкторов классов if stream и ofstream параметр mode имеет значения по умолчанию — соответственно ios: : in и ios: : out.
Закрытие файла
В классах файловых потоков имеется функция close (), которая сбрасывает содержимое потока и закрывает ассоциированный с ним файл.
Кроме того, деструктор потока автоматически закрывает файл при уничтожении объекта потока.
При ошибке закрытия файла устанавливается флаг failbit.
Примеры файловых потоков
Следующий пример (листинг 9.4) демонстрирует различные режимы и способы открытия потока.
Листинг 9.4. Примеры открытия файловых потоков
/////////////////////////////////////////////////////////
//
Filemode.срр: Режимы открытия файлов.
#include #include #pragma hdrstop
#include char *data = {"It"s the first line of test data.",
"Second ,line.",
"Third line.",
"That"s enough!"};
// Функция для распечатки содержимого файла. //
int Print(char *fn) {
char buf ;
ifstream ifs(fn) ;
if (!ifs) {
cout < return -1;
} while (ifs) {
ifs.getline(buf, sizeof(buf)) ;
if (ifs)
cout << buf<< end1;
} return 0;
#pragma argsused
int main(int argc, char* argv)
char name= "Newfile.txt";
fstream fs(name, ios::in);
if (fs) { // Файл уже существует. cout “ name “ " - File already exists."
<< endl;
} else { // Создать новый файл.
cout<< name<< " - Creating new file."<< endl;
fs.open(name, ios::out);
for (int i=0; i<3; i++) fs << data[i] << endl;
fs.close () ;
cout << end1;
// Файл либо уже существовал, либо мы его только что
// создали. Распечатаем его.
// Print(name);
cout << endl;
// Допишем строку в конец файла.
// fs.open(name, ios::app);
if (rs) {
fs M<< data<< endl;
fs.close ();
} Print(name);
return 0;
Рис. 9.3
Результат работы программы Filemode
Для чтения строки из файла мы применили в программе функцию getline () , которая будет подробно описана чуть позже.
Бесформатный ввод-вывод
До сих пор речь у нас шла почти исключительно о вводе-выводе с использованием операций извлечения/передачи данных. Эти операции перегружены для всех встроенных типов и выполняют соответствующие преобразования из внутреннего представления данных в текстовое и из текстового во внутреннее (машинное).
Однако в библиотеке
C++
имеется немало функций
бесформатного
ввода-вывода, которые часто применяют для чтения и записи двоичных (не-текстовых) файлов.
Двоичный режим ввода-вывода
Двоичный режим открытия файла (с установленным битом binary) означает, что никакой трансляции данных при передаче из файла в поток и обратно производиться не будет. Речь здесь идет не о форматных преобразованиях представления данных. При текстовом режиме (он принимается по умолчанию) при передаче данных между файлом и потоком производится замена пар символов CR/LF на единственный символ LF (" n ") и наоборот. Это происходит до преобразований представления, которые выполняются операциями извлечения/передачи. Двоичный ввод-вывод означает всего-навсего, что
такой замены происходить не будет; тем не менее двоичный режим необходим при работе с
сырыми данными,
т. е. данными в машинной форме без преобразования их в текстовый формат.
Чтобы открыть файл в двоичном режиме, нужно, как уже упоминалось, установить в параметре mode конструктора потока или функции open() бит ios::binary.
Чтение и запись сырых данных
Чтение сырых данных производится функцией read () класса istream:
istream &read(char *buf, long len);
Здесь buf — адрес буфера, в который будут читаться данные, а len — число символов, которые нужно прочитать.
Запись сырых данных производится функцией write () класса ostream. Она выглядит точно так же, как функция read () :
ostream &write(char *buf, long len);
Здесь buf — адрес буфера, в котором содержатся данные, а len — число символов, которые должны быть записаны в поток.
ostream os (...);
os.write(...).write (...).write(...) ;
Вот небольшой пример записи и чтения сырых данных:
#include #include int main(void) {
char name = "testfile.dat";
int i = 1234567;
double d = 2.718281828;
// Открытие выходного потока в двоичном режиме
//и запись тестовых данных.
ofstream ofs(name, ios::out | ios::binary);
if (ofs) {
ofs.write((char*)&i, sizeof(i)); // Целое.
ofs.write((char*)&d, sizeof(d)); // Вещественное.
ofs.write(name, sizeof(name)); // Строка. ofs.close ();
// Открытие входного потока в двоичном режиме.
// if stream ifs(name, ios::in | ios::binary) ;
i = 0; //
d = 0; // Уничтожить данные.
name = "\0"; //
if (ifs) {
ifs.read((char*)&i, sizeof (i));
ifs.read((char*)&d, sizeof(d));
ifs.read(name, sizeof(name));
ofs.close () ;
} //
//
Проверка - напечатать прочитанные данные. //