В този урок ще разгледаме основните потоци дефинирани от стандартната библиотека на езика. Това са cin, cout, cerr и clog. Те са достъпни веднага след като добавите следния ред към програмата си:
#iclude <iostream>Какво означават тези странни имена?
- cin – Character input; Cтандартен входен поток;
- cout – Character output; Cтандартен изходен поток;
- cerr – Character error (output); Стандартен поток за грешки (изходен);
- clog – Character log (output); Стандартен поток за лог информация (изходен);
Всички те преобразуват това, което им се подава или това което четат от и към ASCII текст. Тоест спокойно можем да кажем, че са от тип char.
Понятие за поток (stream)
C++ библиотеката използва удобна абстракция наречена поток. За да чете и пише данни байт, по байт. Tехнически е така, защото се използва типа char. И от гледна точка на програмата няма значение към какво е свързан потока щом можем да четем и пишем байт по байт.
Потока е обект, в който програмата може да вмъква или да извлича символи. Няма нужда да знаем подробности за мястото с което е свързан потока. Или някоя от неговите вътрешни характеристики. Всичко, което трябва да знаем е, че потоците са източник / дестинация на символи и че тези символи се четат / пишат последователно (един след друг). Това накратко е концепцията за потоците.
Потоците могат да бъдат свързани към различни обекти: Обикновено пишат и четат в терминалния прозорец, където се изпълнява вашата програма. Но могат да бъдат свързани към фаилове или сокет (socket) за мрежова комуникация.
std::cin
Това е стандартния поток за четене от клавиатурата в терминалния прозорец. Използва се с оператора „>>“. Чете дума или число написано от потребителя. Входа е буфериран. Игнорира интервали, табове и ‘\n’ в началото. Чете до следващия срещнат интервал, таб или ‘\n’. Преобразува прочетеното в желания тип данни фундаментални данни.
int a {};
double b {};
std::string c {};
std::cin >> a;
std::cin >> b;
std::cin >> c;Четене на цял ред:
std::string s;
std::getline(std::cin, s);std::cout
Стандартен изход към терминала. Изхода е буфериран. Защото се смята, че изходните операции са тежки. И това е вярно донякъде но зависи от ОС. Ако искате бързина не използвайте постоянно endln оператора. А го извикайте след няколко операции. За нов ред добавете ‘\n’. Когато буфера се напълни сам ще изплюе текста към терминала.
std::cerr
Това е небуфериран поток за грешки. Отпечатва се веднага в терминала.
std::clog
Това е стандартният поток за логовaне на съобщения на програмата и диагностична информация. Използва се рядко. Изхода е буфериран.
Обобщение
| Клас | Буфериране | Действие |
| std::cin | ✅ | вход на данни |
| std::cout | ✅ | извеждане на данни |
| std::cerr | ❌ | извеждане на грешки |
| std::clog | ✅ | логове и debug |
Грешки в четенето
Изходния поток почти никога не ни дава грешка. Но при четене на входни данни входния поток може да изпадне в грешка. Нека обясним. Имаме:
int x {};
std::cin >> x;Но, ако потребителя е въвел текст вместо число? Какво става? Входния поток пропада в безкраен цикъл, опитвайки се да прочете нещо и никога не успява. Тогава трябва да изчистим буфера и грешките преди да прочетем нов вход:
std::cin.clear(); // маха fail флага
std::cin.ignore(10'000, '\n'); // изхвърля грешния входФлагове за състоянието на потока
Потоците в C++ стандартната библиотека са конфигурирани да не хвърлят изключения по подразбиране. Те работят подобно на входно – изходните операции в езика C. Имат флагове за състоянието си. На C++ се пишат игри и драйвери. Дори части от ядрото на ОС. Помислете какво би станало, ако в програма на C++ се изпълняват няколко паралелни нишки и една от тях хвърли изключение?
Любопитно?
В C++ всяка нишка (std::thread) има собствен стек за изпълнение. Ако в нишка бъде хвърлено изключение и то не бъде хванато вътре в същата нишка програмата ще извиква: std::terminate().
Основни флагове за грешка
| Флаг | Значение |
| goodbit | Няма грешка |
| eofbit | Достигнат е край на файла |
| failbit | Невалиден вход / логическа грешка |
| badbit | Сериозна грешка в потока |
Проверка на състоянието
if(std::cin.good())
std::cout << "Всичко е наред" << std::endl;
if(std::cin.fail())
std::cout << "Грешка при четене" << std::endl;
if(std::cin.eof())
std::cout << "Край на файла" << std::endl;
if(std::cin.bad())
std::cout << "Критична грешка" << std::endl;Ето пример, който проверява състоянието на входния поток:
#include <iostream>
int ReadInt() {
int x;
while( not (std::cin >> x)) {
std::cout << "Невалидно число!" << std::endl;
std::cin.clear();
std::cin.ignore(10'000, '\n');
}
return x;
}
int main() {
int x = {};
x = ReadInt();
std::cout << "x = " << x << std::endl;
}Член – функцията rdstate()
Този метод връща състоянието на потока като цяло число. И може да съдържа повече от една грешка. Всяка възникнала грешка в потока задава отделен бит в числото. И тогава резултата не е 0. Когато метода върне 0, тогава потока е в добро, работещо състояние.
Като използвате долния код въведете цяло число и после текст. Какъв ще е изхода?
#include <iostream>
int main() {
int x = {};
std::cin >> x;
std::cout << "rdstate() = " << std::cin.rdstate() << std::endl;
}Поведение на потоците
