Насколько медленны iostreams?
Потоки ввода-вывода в стандартной библиотеке C++ просты в использовании, типобезопасны, устойчивы к утечке ресурсов, и позволяют простую обработку ошибок. Однако, за ними закрепилась репутация «медленных». Этому есть несколько причин, таких как широкое использование динамической аллокации и виртуальных функций. Вообще, потоки — одна из самых древних частей стандартной библиотеки (они начали использоваться примерно в 1988 году), и многие решения в них сейчас воспринимаются как «спорные». Тем не менее, они широко используются, особенно когда надо написать какую-то простую программу, работающую с текстовыми данными.
Вопрос производительности iostreams не праздный. В частности, с проблемой производительности консольного ввода-вывода можно столкнуться в системах спортивного программирования, где даже применив хороший алгоритм, можно не пройти по времени только из-за ввода-вывода. Я также встречался с этой проблемой при обработке научных данных в текстовом формате.
C++ Урок 5. Файл iostream
Сегодня в комментариях у посту возникло обсуждение о медленности iostreams. В частности, freopen пишет
Забавно смотреть на ваши оптимизации, расположенные по соседству со считыванием через cin 🙂
а aesamson даёт такую рекомендацию
Можно заменить на getchar_unlocked() для *nix или getchar() для всех остальных.
getchar_unlocked > getchar > scanf > cin, где «>» означает быстрее.
В этом посте я развею и подтвержу некоторые мифы и дам пару рекомендаций.
Все измерения в этом посте приведены для системы Ubuntu 14.10 с компилятором GCC 4.9.1, компилировалось с ключами
g++ -Wall -Wextra -std=c++11 -O3
Запуск проводился на ноутбуке с процессором Intel Core2 Duo P8600 (2.4 ГГц).
Постановка задачи
В спортивном программировании, как и в UNIX-way, обычно входные данные подаются на входной поток. Итак, задача:
На входной поток (stdin) поступает много неотрицательных целых чисел по одному на строке. Программа должна вывести максимальное из входных чисел.
Сформируем входные данные
seq 10000000 > data
В файл data мы записали 10 миллионов последовательных целых чисел, общим объёмом 76 мегабайт.
Запускать программу мы будем так
time ./a.out < data
1. scanf
Решим задачу с использованием старого доброго scanf.
int max_scanf() < int x; int max = -1; while (scanf(«%d», max = std::max(x, max); >return max; >
При использовании scanf важно не забыть всегда проверять возвращаемое значение — это количество реально прочитанных и заполненных аргументов (GCC с -Wall напомнит об этом). В нашем случае при успешном чтении возвращаемое значение должно равняться 1.
Функция main
int main()
Время работы: 1.41 c
2. Наивный std::cin
Теперь решим задачу самым простым способом при помощи iostreams:
Структура и порядок выполнения программы. #Include. iostream. using namespace.
int max_iostream(std::istream int x; int max = -1; while(f >> x) max = std::max(x, max); return max; >
Время работы: 4.41 c
Ого! Потоки оказались медленнее чем scanf в 3 раза! То есть выходит, что iostream оказываются действительно никуда не годится по скорости?
3. Быстрый std::cin
На самом деле, чтобы исправить ситуацию, достаточно добавить в программу одну единственную строчку. В самом начале функции main вставим:
std::ios::sync_with_stdio(false);
Что это значит?
Для того, чтобы в программе можно было смешивать iostreams и stdio, была введена эта синхронизация. По умолчанию, при работе со стандартными потоками ( std::cin, std::cout, std::cerr . ) буфер сбрасывается после каждой операции ввода-вывода, чтобы данные не перемешивались. Если же мы предполагаем пользоваться только iostream, то мы можем отключить эту синхронизацию. Подробнее можно почитать на cppreference.
Время работы: 1.33 c
Совсем другое дело! Более того, это быстрее, чем scanf! То есть, не все так плохо. К плюсам же iostreams можно отнести простоту использования, типобезопасность и более легкую обработку ошибок.
Все последующие варианты с использованием std::cin будут использовать эту оптимизацию.
4. Наивный std::istringstream
Помимо ввода из файла, стандартная библиотека предоставляет также классы для ввода из строки с таким же интерфейсом. Посмотрим, насколько это медленно. Будем читать из входного потока по одной строке, а затем парсить её с помощью std::istringstream :
int max_iostream_iss(std::istream int x; int max = -1; std::string line; while (std::getline(f, line)) < std::istringstream iss(line); if(! (iss >> x)) break; max = std::max(x, max); > return max; >
Время работы: 7.21 c
Очень медленно!
5. Переиспользование std::istringstream
Может показаться удивительным, но самое медленное в istringstream — это его создание. А мы создаём для каждой входной строки заново. Попробуем переиспользовать один и тот же объект:
int max_iostream_iss_2(std::istream int x; int max = -1; std::string line; std::istringstream iss(line); while (std::getline(f, line)) < iss.clear(); // Сбрасываем флаги ошибок iss.str(line); // Задаём новый буфер if(! (iss >> x)) break; max = std::max(x, max); > return max; >
Обратите внимание, что нужны 2 вызова — clear, чтобы сбросить флаги состояния, и str, чтобы задать новый буфер, из которого будет происходить чтение.
Время работы: 2.16 c
Это другое дело. Это ожидаемо медленнее, чем чтение напрямую из std::cin (данные проходят 2 раза через классы потоков), но не катастрофично.
6. Хотим ещё быстрее! (getchar/getchar_unlocked)
Что делать, если производительности все равно не хватает? Использовать более низкоуровневые средства ввода-вывода и кастомный парсер. В комментариях к упомянутому топику aesamson привел пример кода, реализующего простейший парсер целых чисел (вероятно, взятый со StackOverflow). Для чтения из входного потока используется getchar_unlocked — потоконебезопасная версия getchar . Я добавил пропуск лишних символов и простейшую обработку конца файла:
bool read_int_unlocked(int int c = getchar_unlocked(); int x = 0; int neg = 0; for (; !(‘0’if (c == ‘-‘) < neg = 1; c = getchar_unlocked(); >if (c == EOF) return false; for (; ‘0’ out = neg ? -x : x; return true; > int max_getchar_unlocked()
Время работы: getchar 0.82 с, getchar_unlocked 0.28 с!
Очень хороший результат! И видно, насколько велико замедление из-за блокировок ради потокобезопасности.
Но у такого подхода есть минусы — необходимо писать парсеры для всех используемых типов данных (а это уже не так просто даже для чисел с плавающей запятой), сложность обработки ошибок, потоконебезопасность в случае getchar_unlocked . Альтернативно — можно попробовать воспользоваться генератором парсеров, например re2c , boost::Spirit::Qi , и т.д. (много их).
7. C++11: std::stoi
Спасибо Lol4t0 что напомнил в комментариях про появившиеся в C++11 функции std::stoi/std::stol/std::stoll . Будем читать по одной строке с помощью getline, а затем парсить её с помощью stol. Код будет выглядеть так:
int max_stoi(std::istream int max = -1; std::string line; while (std::getline(f, line)) < int x = std::stoi(line); max = std::max(x, max); >return max; >
Время работы: 1.04 c
Это самый быстрый стандартный способ чтения целых чисел. (А для чисел с плавающей точкой есть аналогичные функции stof/stod).
8. Бонус: Чтение большими блоками + Boost::Spirit
Попробуем написать Самый Быстрый вариант. Будем читать входные данные большими блоками и затем парсить с помошью Boost::Spirit::Qi, который заявляется как генератор очень быстрых парсеров. Это compile-time генератор: мы описываем грамматику на С++ в нотации, приближенной к BNF, и во время компиляции с помощью магии метапрограммирования генерируется парсер.
Код (внимание, boost и метапрограммирование detected!)
#include #include #include #include template Iterator max_parser(Iterator first, Iterator last, int namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace phoenix = boost::phoenix; using qi::int_; using qi::_1; using ascii::space; using phoenix::ref; using phoenix::if_; using qi::eoi; using qi::lexeme; bool r = qi::phrase_parse(first, last, // Begin grammar ( *lexeme[int_ >> (!eoi)][if_(_1 > ref(max))[ref(max) = _1]] ) , // End grammar space); return first; > int max_spirit(std::istream size_t chunk_size = 1 buffer(new char[2*chunk_size]); char * middle = buffer.get() + chunk_size; char * begin = middle; int max = -1; while(true) < f.read(middle, chunk_size); if (f.gcount() == 0) break; char * end = middle + f.gcount(); char * last = max_parser(begin, end, max); if (last < middle) break; // copy the remaining data just before the middle: begin = middle — (end — last); std::copy(last, end, begin); >return max; >
Время работы: 0.18 c
Это рекорд!
Результаты и советы
Время работы:
1 | scanf | 1.41 | 1.48 | |
2 | std::cin | 4.41 | 13.30 | |
3 | std::cin и std::ios::sync_with_stdio(false) | 1.33 | 13.24 | |
4 | std::istringstream | 7.21 | 9.16 | |
5 | std::istringstream с переиспользованием | 2.16 | 7.92 | |
6a | getchar | 0.82 | 0.84 | 9.14 |
6b | getchar_unlocked | 0.28 | 0.26 | 2.94 |
7 | std::getline + std::stoi | 1.04 | 3.53 | 10.8 |
8 | Большой блок + Boost::Spirit | 0.18 | 1.67 | 1.90 |
* — Измерения на файле со 100 миллионами чисел (размер файла 848 мегабайт).
Рекомендации:
- В C++11 самый быстрый стандартный способ чтения чисел из потока — std::getline + std::stol (в сочетании с sync_with_stdio(false) , если используются стандартный поток std::cin ). Этот способ заметно быстрее чем scanf и уступает только способам с getchar.
- Для того, чтобы ускорить std::cin/std::cout , можно использовать std::ios::sync_with_stdio(false); При этом скорость станет сравнимой или лучше чем scanf. (Только убедитесь, что вы не смешиваете потоковые и stdio операции на одном и том же потоке)
- У istringstream очень медленный конструктор. Поэтому производительность можно серьёзно поднять если переиспользовать объект потока.
- Большей производительности можно добиться, используя getchar_unlocked (или просто getchar , если нужна потокобезопасность) и кастомный парсер.
- Ещё большей производительности можно достигнуть, если читать данные большими кусками и работать затем исключительно в памяти.
Update 1. По совету Lol4t0 добавлен метод номер 7.
Update 2. В таблицу добавлены времена выполнения на clang+libc++ (версия 3.5.0, выполнялось на той же системе). Видно, что производительность потоков очень плохая, да к тому же трюк с выключением синхронизации не работает. В результате stoi оказывается в 2 раза медленнее чем scanf.
Update 3. Добавлен вариант номер 8: чтение большими блоками и разбор с помощью Boost::Spirit. И это чемпион!
Источник: habr.com
1.5 – Знакомство с iostream: cout, cin и endl
В этом уроке мы подробнее поговорим о std::cout , который мы использовали в нашей программе «Hello world!» для вывода в консоль текста «Hello world!». А также узнаем, как получить данные от пользователя, которые мы будем использовать, чтобы сделать наши программы более интерактивными.
Библиотека ввода/вывода
Библиотека ввода/вывода (библиотека io) является частью стандартной библиотеки C++, которая имеет дело с базовым вводом и выводом. Мы будем использовать функции этой библиотеки для получения ввода с клавиатуры и вывода данных в консоль. Буквы io в iostream означают «input/output» (ввод/вывод).
Чтобы использовать функции, определенные в библиотеке iostream , нам нужно включить заголовочный файл iostream в начало любого исходного файла, который использует содержимое, определенное в iostream , например:
#include // остальной код использует функциональность iostream
std::cout
Библиотека iostream содержит несколько предопределенных переменных, которые мы можем использовать. Одной из наиболее полезных является std::cout , которая позволяет нам отправлять данные в консоль для печати в виде текста. cout означает «character output» (вывод символов).
Вспомним нашу программу Hello world :
#include // для std::cout int main() < std::cout
В эту программу мы включили iostream , чтобы у нас был доступ к std::cout . Внутри нашей функции main() мы используем std::cout вместе с оператором вставки ( для отправки текста « Hello world! » в консоль для печати.
std::cout может печатать не только текст, но и числа:
#include // для std::cout int main() < std::cout
Это дает результат:
Его также можно использовать для вывода значений переменных:
#include // для std::cout int main() < int x; // определяем целочисленную переменную x, инициализированную значением 5 std::cout
Это дает результат:
Чтобы напечатать несколько элементов в одной строке, для объединения (связывания) нескольких частей выводимых данных, оператор вставки (
#include // для std::cout int main()
Эта программа печатает:
Hello world!
Вот еще один пример, в котором мы печатаем и текст, и значение переменной в одном выражении:
#include // для std::cout int main() < int x; std::cout
Эта программа печатает:
x is equal to: 5
std::endl
Как вы думаете, что напечатает следующая программа?
#include // для std::cout int main()
Результат может вас удивить:
Hi!My name is Alex.
Отдельные выражения вывода не приводят к отдельным выводимым строкам в консоли.
Если мы хотим выводить в консоль отдельные выводимые строки, нам нужно указать консоли, когда необходимо переместить курсор на следующую строку.
Один из способов сделать это – использовать std::endl . При выводе с помощью std::cout , std::endl выводит в консоль символ новой строки (заставляя курсор переместиться в начало следующей строки). В этом контексте endl означает «end line» (конец строки).
#include // для std::cout и std::endl int main() < std::cout
Hi! My name is Alex.
Совет
В приведенной выше программе второй std::endl технически не нужен, так как программа сразу же после этого завершается. Однако он служит двум полезным целям: во-первых, он помогает указать, что строка вывода является «законченной мыслью». Во-вторых, если мы позже захотим добавить дополнительные выражения вывода, нам не нужно будет изменять существующий код. Мы можем просто добавить новые выражения.
std::endl против ‘n’
Использование std::endl может быть немного неэффективным, поскольку фактически выполняет две задачи: перемещает курсор на следующую строку и «очищает» вывод (обеспечивает немедленное отображение на экране). При записи текста в консоль с использованием std::cout , std::cout обычно всё равно очищает вывод (а если нет, это обычно не имеет значения), поэтому наличие очистки от std::endl редко бывает важным.
Из-за этого обычно предпочтительнее использовать символ ‘n’ . Символ ‘n’ перемещает курсор на следующую строку, но не выполняет избыточную очистку, поэтому он работает лучше. Символ ‘n’ также легче читать, поскольку он короче и может быть встроен в существующий текст.
Вот пример, в котором ‘n’ используется двумя разными способами:
#include // для std::cout int main() < int x; // Использование ‘n’ в одиночном режиме std::cout
Этот код напечатает:
x is equal to: 5 And that’s all, folks!
Обратите внимание, что когда ‘n’ используется сам по себе для перемещения курсора на следующую строку, необходимы одинарные кавычки. При встраивании в текст, который уже заключен в двойные кавычки, одинарные кавычки не нужны.
Мы рассмотрим, что такое ‘n’ более подробно, когда перейдем к уроку о символах (4.11 – Символы).
Лучшая практика
При выводе текста в консоль лучше использовать ‘n’ вместо std::endl .
Предупреждение
‘n’ использует обратный слеш (как и все специальные символы в C++), а не прямой слеш. Использование прямого слеша (например, ‘/n’ ) может привести к неожиданному поведению.
std::cin
std::cin – еще одна предопределенная переменная, определенная в библиотеке iostream . В то время как std::cout выводит данные в консоль с помощью оператора вставки ( оператора извлечения ( >> ). Для дальнейшего использования входные данные должны быть сохранены в переменной.
#include // для std::cout и std::cin int main() < // запрашиваем у пользователя число std::cout ; // получаем число с клавиатуры и сохраняем его в переменной x std::cin >> x; std::cout
Попробуйте скомпилировать эту программу и запустить ее самостоятельно. Когда вы запустите программу, строка 6 напечатает «Enter a number: ». Когда код дойдет до строки 10, ваша программа будет ждать, пока вы введете данные. После того, как вы введете число (и нажмете клавишу Enter ), введенное вами число будет присвоено переменной x . Наконец, в строке 11 программа напечатает «You entered », а затем число, которое вы только что ввели.
Например (я ввел 4):
Enter a number: 4 You entered 4
Это простой способ получить от пользователя ввод с клавиатуры, и в будущем мы будем использовать его во многих наших примерах. Обратите внимание, что вам не нужно использовать ‘n’ при принятии входных данных, так как пользователю нужно будет нажать клавишу Enter , чтобы его входные данне были принят, а это переместит курсор на следующую строку.
Если ваш экран закрывается сразу после ввода числа, обратитесь к уроку «0.8 – Несколько основных проблем C++» для решения этой проблем.
Лучшая практика
Существуют споры о том, нужно ли инициализировать переменную непосредственно перед тем, как передать ей значение, предоставленное пользователем, через другой источник (например, std::cin ), поскольку значение, предоставленное пользователем, просто перезапишет значение инициализации. В соответствии с нашей предыдущей рекомендацией о том, что переменные всегда следует инициализировать, лучше всего сначала инициализировать переменную.
Мы обсудим, как std::cin обрабатывает недопустимые входные данные в следующем уроке (7.16 – std::cin и обработка недопустимых входных данных).
Для продвинутых читателей
Библиотека ввода/вывода C++ не позволяет принимать ввод с клавиатуры без нажатия пользователем клавиши Enter . Но если вам это нужно, то вам придется использовать стороннюю библиотеку. Для консольных приложений мы рекомендуем библиотеку pdcurses. Многие графические пользовательские библиотеки имеют для этого свои собственные функции.
Резюме
Программисты-новички часто путают std::cin , std::cout , оператор вставки ( > ). Вот простой способ запомнить:
- std::cin и std::cout всегда идут в левой части выражения;
- std::cout используется для вывода значения (cout = character output);
- std::cin используется для получения входного значения (cin = character input);
- >> используется с std::cin и показывает направление движения данных (если std::cin представляет клавиатуру, входные данные перемещаются с клавиатуры в переменную). std::cin >> x перемещает значение, введенное пользователем с клавиатуры, в x .
Подробнее об операторах мы поговорим в уроке «1.9 – Знакомство с литералами и операторами».
Небольшой тест
Вопрос 1
Рассмотрим следующую программу, которую мы использовали выше:
#include // для std::cout и std::cin int main() < // запрашиваем у пользователя число std::cout ; // получаем число с клавиатуры и сохраняем его в переменной x std::cin >> x; std::cout
Программа ожидает, что вы введете целочисленное значение, поскольку переменная x , в которую будет помещен пользовательские входные данные, является целочисленной переменной.
Запустите эту программу несколько раз и опишите, что произойдет, если вместо этого вы введете следующие типы входных данных:
а) Буква, например, h .
б) Число с дробной частью. Попробуйте числа с дробными составляющими меньше 0,5 и больше 0,5 (например, 3,2 и 3,7).
Дробная составляющая опущена.
в) Небольшое отрицательное целое число, например -3.
Всё прекрасно работает.
г) Слово, например «Hello».
д) Действительно большое число (минимум 3 миллиарда).
Вы получаете, казалось бы, случайное число.
Последнее предложение может быть особенно неожиданным. Попробуйте! Это происходит потому, что x может содержать числа только до определенного размера. После этого он «переполняется». Мы обсудим переполнение в следующем уроке.
Теги
Сохранить или поделиться
На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.
В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.
Присоединяйтесь к нам во ВКонтакте!
- Антенны и распространение радиоволн
- Беспроводная связь
- Высокочастотная техника
- Волоконно-оптические линии связи (ВОЛС)
- Измерительная техника
- Исполнительные механизмы и драйверы
- САПР
- Спутниковая связь
- Схемотехника
- Телевидение
- Цифровая электроника
- Технологии и инструменты
- Электронные компоненты
- А что внутри?
- Прочее (радиоэлектроника)
- Алгоритмы и структуры данных
- Базы данных
- Веб-разработка
- Мультимедиа (разработка ПО)
- Нейронные сети
- Паттерны проектирования
- Связь
- Языки программирования
- Компьютерные сети
- Операционные системы
- Верстка
- Системы контроля версий
- История технологий
- Мультимедиа
- Новости телекома
- Нормативная документация
- Охрана труда
- Полезные программы
- Просто интересно
- Экономика телекоммуникаций и электронной промышленности
- Экономика и инвестиции
Мы в соцсетях
Источник: radioprog.ru
Язык C++ глазами чайника
Как и в старичке C, в C++ нет встроенных средств для ввода и вывода данных куда бы то ни было. Редкие в наше время программисты на C++ под MS-DOS, да и вообще старшее поколение программистов, могут возразить – мол, ввод и вывод суть чтение и запись по определенным адресам в оперативной памяти (угу, а программирование суть написание машинных команд). Однако это мало того что неудобно, так еще и обладает такими нехорошими свойствами, как абсолютная платформенная непереносимость, а также несовместимость с объектно-ориентированной парадигмой. Короче говоря, по абсолютным адресам в памяти мы писать не хотим, и волшебные слова «контроллер», «DMA» и «драйвер» оставим тем, кому это интересно.
К счастью, добрые люди присобачили к C++ стандартную библиотеку, выдержанную в традициях ООП и позволяющую все, что только душа пожелает (ну, почти).
В этой и нескольких следующих статьях я собираюсь дать небольшое обзорное описание библиотеки iostream , но несколько нетрадиционное. Обычно авторы книг по C++ начинают с элементарных
cout
Каждый уважающий себя компилятор C++ поставляется со стандартной библиотечкой ввода-вывода по имени iostream . Ключевым понятием в этой библиотеке является поток (stream) – не путать с потоком управления (thread)! Не вдаваясь в детали реализации, можно сказать, что поток – это объект, который может служить источником или приемником байтов (или и тем, и другим сразу). Не все сразу могут понять отличие потоковых средств ввода-вывода от традиционных read/write . Суть этих отличий в том, что поток – это более высокая ступень абстракции; он может использоваться как, например, для чтения из файла (и при этом он ведет себя почти как обычный read ), так и для чтения данных из последовательного порта или с клавиатуры. В последнем случае данные уже имеют ярко выраженную потоковую природу.
Кстати, надо заметить, что консерваторы по-прежнему могут использовать привычную, аки домашние тапочки, библиотеку stdio , но в таком получается меньше контроля над потоками, меньше удобства и меньше соответствия парадигме ООП.
Для начала посмотрим, какие в библиотеке iostream есть заголовочные файлы:
- fstream – классы, инкапсулирующие ввод-вывод на внешних файлах.
- ios – базовые классы iostream ; обычно этот заголовочный файл не нужно включать напрямую, он включается в других заголовочниках.
- iostream – определяет объекты ввода-вывода на стандартных потоках.
- ostream – определяет шаблонный базовый класс basic_ostream , управляющий выводом в потоки; этот файл также обычно не включается в программу напрямую.
- streambuf – определяет шаблонный базовый класс basic_streambuf , управляющий буферизацией; не нужно включать напрямую.
- iomanip – определяет ряд манипуляторов (про них ниже).
- iosfwd – объявляет (но не определяет!) кучу шаблонных классов и typedef’ов.
- istream – брат-близнец ostream, только в отношении потокового ввода.
- sstream – определяет ряд классов для потоковой работы с хранящимися в памяти последовательностями.
- strstream – определяет ряд классов для потоковой работы с char-строками.
Как нетрудно догадаться, большую часть этих заголовочников нормальный программист никогда в своей жизни не увидит (и хорошо). Обычно используются только fstream, iostream, iomanip, sstream, strstream и соответствующие им классы.
Теперь рассмотрим структуру и использование одной из частей библиотеки iostream, а именно классы для потокового ввода-вывода на файлах. Работа с ними ничуть не отличается от потокового ввода-вывода на памяти, на строках или на стандартных потоках (то есть консоли и клавиатуре).
Заголовочный файл fstream определяет ряд классов: filebuf, wfilebuf, fstream, wfstream, ifstream, wifstream, ofstream, wofstream, basic_filebuf, basic_fstream, basic_ifstream, basic_ofstream . На самом деле из приведенного списка только последние четыре пункта – классы. Они определяются следующим образом:
template >
class basic_filebuf : public basic_streambuf
template >
class basic_fstream : public basic_iostream
template >
class basic_ifstream : public basic_istream
template >
class basic_ofstream : public basic_ostream
Они очень похожи – шаблонные классы с двумя параметрами: Elem – собственно, тип вводимо-выводимых элементов, и Tr – класс символьных характеристик этих элементов. Tr обязательно должен быть типа char_traits (лучше оставлять указанное по умолчанию char_traits ).
Остальные имена из приведенного выше списка – всего лишь специализации этих классов; например, классы ofstream и wofstream , отвечающие, соответственно, за вывод в файл последовательностей char- и wchar- символов, определяются так:
typedef basic_ofstreamchar> > ofstream;
typedef basic_ofstreamwchar_t, char_traitswchar_t> > wofstream;
Источник: sleepylearner.blogspot.com
Iostream c что это за программа
Примеры использования потока вывода:
int i = 10; char c = ‘A’; char cs[] = «C string»; bool b = true; double d = 3.14159265; string s = «STL string»; cout i endl; cout c ‘B’ endl; cout cs endl b endl; cout d endl; cout s endl;
10 AB C string 1 3.14159 STL string
struct Book string title, author; int num_pages; ostream(ostreamauthor <» » <title <» (» <num_pages <«)»); > >;
Некоторые методы класса ostream:
- put(char c) — записать символ с в поток
- write(const char* s, streamsize n) — записать первые n элементов массива s в поток (streamsize представляет целое число со знаком, например, int)
- flush() — записать значение из буфера
- close() — закрытие потока
Потоки ввода (istream)
Для ввода используется оператор >> . Он также определен для всех встроенных типов и некоторых классов стандартной библиотеки.
Оператор >> также можно определить для своих классов:
istream>(istream
Некоторые методы класса istream:
- get() — считать следующий символ
- get(char *buf, streamsize n) — считать максимум n-1 символ и поместить в массив buf
- get(char *buf, streamsize n, char delim) — считывание символов до символа-разделителя delim (разделитель не считывается и остается в потоке)
- getline(char *buf, streamsize n)
- getline(char *buf, streamsize n, char delim)
- peek() — считывает следующий символ, но оставляет его в потоке
- ignore(streamsize n = 1, int delim = EOF) — извлекает символы из потока до тех пор, пока их число меньше n или пока не встретился символ delim
- putback(char c) — добавляет символ с в текущую позицию потока
- unget() — возвращает последний считанный символ в поток
Форматирование
Для управления форматом вывода можно устанавливать специальные флаги потока методом setf(ios_base::fmtflags f) . Но удобнее пользоваться манипуляторами — специальными функциями, реализованными в заголочных файлах , (они по умолчанию включены в ) и .
Основные манипуляторы ввода/вывода:
- boolalpha — стороковое представление логических значений
- noboolalpha — числовое представление логических значений
cout boolalpha true endl; cout noboolalpha false;
true 0
- showbase — включает вывод 0 перед восьмеричными и 0x перед шестнадцатеричными числами
- noshowbase — выключает вывод 0 и 0x
- dec — вывод чисел в десятичной системе счисления
- oct — в восьмеричной
- hex — в шестнадцатеричной
- uppercase — заглавные буквы в записи шестнадцатеричных чисел и чисел с плавающей запятой в научной записи
- nouppercase — строчные буквы в записи чисел
cout showbase uppercase; cout oct 0777 ‘ ‘; cout hex 0xABC endl; cout noshowbase nouppercase; cout oct 0777 ‘ ‘; cout hex 0xABC endl;
0777 0XABC 777 abc
- skipws — пропуск символов-разделителей ( ‘ ‘, ‘t’, ‘n’, и т.п. )
- noskipws — выключение пропуска разделителей
- setw(int n) — определяет минимальное количество символов, которые выведутся следующей операцией вывода
- setfill(char c) — символ-заполнитель
- left — выравнивание поля по левому краю
- right — выравнивание поля по правому краю
- internal — выравнивание поля по ширине
cout setw(10); cout setfill(‘#’); cout 1 endl; cout setw(5); cout 100 endl; cout left setw(10) -100 endl; cout right setw(10) -100 endl; cout internal setw(10) -100 endl;
#########1 ##100 -100###### ######-100 -#######10
- scientific — научная запись для чисел с плавающей запятой
- fixed — фиксированная точность для чисел с плавающей запятой
- setprecision — точность вывода чисел (по умолчанию равна 6)
cout 1234.5678 endl; cout scientific 1234.5678 endl; cout fixed 1234.5678 endl; cout setprecision(4); cout 1234.5678 endl; cout scientific 1234.5678 endl; cout fixed 1234.5678 endl;
1234.57 1.234568e+003 1234.567800 1234.5678 1.2346e+003 1234.5678
- endl — запись n и очистка буфера
- ends — запись
- flush — очистка буфера потока
- ws — прочитать и проигнорировать символы-разделители
Сотояние потока
Каждый поток istream или ostream имеет связанное с ним состояние.
Методы проверки состояния:
- good() — можно выполнить следующую операцию
- eof() — конец потока
- fail() — следующая операция не выполнится
- bad() — поток испорчен
Стандартные потоки — iostream
Для реализации стандартного ввода/вывода в библиотеку C++ включен заголовочный файл iostream , содержащий следующие предопределенные объекты потоков:
- cin — стандартный поток ввода (соответствует потоку C stdin)
- cout — стандартный поток вывода (соответствует stdout)
- cerr — стандартный поток вывода ошибок (соответствует stderr)
- clog — стандартный поток вывода журнала (соответствует stderr)
Файловые потоки — fstream
Файловые потоки расположены в заголовочном файле . ifstream — поток ввода, ofstream — поток вывода.
Имя файла передается потоку либо в конструкторе, либо через вызов метода open .
ofstream out_1(«test1.txt»); ofstream out_2; out_2.open(«test2.txt»); . out_1.close(); out_2.close();
Строковые потоки — sstream
Строковые потоки расположены в заголовочном файле .
istringstream — поток ввода. Строка передается потоку в конструкторе.
ostringstream — поток вывода. Строку-результат возвращает метод str() .
string str = «1 2 3»; istringstream iss(str); ostringstream oss; int a, b, c; iss >> a >> b >> c; oss a endl; oss setw(5) setfill(‘#’); oss b endl; oss c; cout oss.str();
1 ####2 3
Code More Team — GitHub
Источник: codemore.ru