Что такое cin c

Поточный ввод-вывод в C++

Поточный ввод-вывод в C++ выполняется с помощью функций сторонних библиотек. В С++, как и в С, нет встроенных в язык средств ввода-вывода.

Библиотека iostream определяет три стандартных потока:

Для их использования в Microsoft Visual Studio необходимо прописать строку:

Для выполнения операций ввода-вывода переопределены две операции поразрядного сдвига:

Возможно многократное назначение потоков:
cout

Ввод информации

При этом из входного потока читается последовательность символов до пробела, затем эта последовательность преобразуется к типу идентификатора, и получаемое значение помещается в идентификатор:

Возможно многократное назначение потоков:
cin >> переменная1 >> переменная2 >>. >> переменнаяn;

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

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

Результат выполнения
Что такое cin c. Смотреть фото Что такое cin c. Смотреть картинку Что такое cin c. Картинка про Что такое cin c. Фото Что такое cin c

Для ввода текста до символа перевода строки используется манипулятор потока getline() :

Результат выполнения
Что такое cin c. Смотреть фото Что такое cin c. Смотреть картинку Что такое cin c. Картинка про Что такое cin c. Фото Что такое cin c

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

В С++ имеется ряд манипуляторов. Рассмотрим основные:

МанипуляторОписание
endlПомещение в выходной поток символа конца строки ‘\n’
decУстановка основания 10-ой системы счисления
octУстановка основания 8-ой системы счисления
hexУстановка основания 16-ой системы счисления
setbaseВывод базовой системы счисления
width(ширина)Устанавливает ширину поля вывода
fill(‘символ’)Заполняет пустые знакоместа значением символа
precision(точность)Устанавливает количество значащих цифр в числе (или после запятой) в зависимости от использования fixed
fixedПоказывает, что установленная точность относится к количеству знаков после запятой
showposПоказывает знак + для положительных чисел
scientificВыводит число в экспоненциальной форме
get()Ожидает ввода символа
getline(указатель, количество)Ожидает ввода строки символов. Максимальное количество символов ограничено полем количество

Пример Программа ввода-вывода значения переменной в C++

Та же программа, написанная на языке Си

Пример Использование форматированного вывода

Результат выполнения
Что такое cin c. Смотреть фото Что такое cin c. Смотреть картинку Что такое cin c. Картинка про Что такое cin c. Фото Что такое cin c

Еще один пример использования форматированного вывода: для t∈[0;3] с шагом 0,5 вычислить значение y=cos(t).

Результат выполнения
Что такое cin c. Смотреть фото Что такое cin c. Смотреть картинку Что такое cin c. Картинка про Что такое cin c. Фото Что такое cin c

Источник

1.5 – Знакомство с iostream: cout, cin и endl

Библиотека ввода/вывода

Библиотека ввода/вывода (библиотека io) является частью стандартной библиотеки C++, которая имеет дело с базовым вводом и выводом. Мы будем использовать функции этой библиотеки для получения ввода с клавиатуры и вывода данных в консоль. Буквы io в iostream означают «input/output» (ввод/вывод).

std::cout

Вспомним нашу программу Hello world :

std::cout может печатать не только текст, но и числа:

Это дает результат:

Его также можно использовать для вывода значений переменных:

Это дает результат:

Чтобы напечатать несколько элементов в одной строке, для объединения (связывания) нескольких частей выводимых данных, оператор вставки ( ) можно использовать несколько раз в одном выражении. Например:

Эта программа печатает:

Вот еще один пример, в котором мы печатаем и текст, и значение переменной в одном выражении:

Эта программа печатает:

std::endl

Как вы думаете, что напечатает следующая программа?

Результат может вас удивить:

Отдельные выражения вывода не приводят к отдельным выводимым строкам в консоли.

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

Совет

В приведенной выше программе второй std::endl технически не нужен, так как программа сразу же после этого завершается. Однако он служит двум полезным целям: во-первых, он помогает указать, что строка вывода является «законченной мыслью». Во-вторых, если мы позже захотим добавить дополнительные выражения вывода, нам не нужно будет изменять существующий код. Мы можем просто добавить новые выражения.

std::endl против ‘\n’

Вот пример, в котором ‘\n’ используется двумя разными способами:

Этот код напечатает:

Обратите внимание, что когда ‘\n’ используется сам по себе для перемещения курсора на следующую строку, необходимы одинарные кавычки. При встраивании в текст, который уже заключен в двойные кавычки, одинарные кавычки не нужны.

Мы рассмотрим, что такое ‘\n’ более подробно, когда перейдем к уроку о символах (4.11 – Символы).

Лучшая практика

Предупреждение

‘\n’ использует обратный слеш (как и все специальные символы в C++), а не прямой слеш. Использование прямого слеша (например, ‘/n’ ) может привести к неожиданному поведению.

std::cin

Если ваш экран закрывается сразу после ввода числа, обратитесь к уроку «0.8 – Несколько основных проблем C++» для решения этой проблем.

Лучшая практика

Существуют споры о том, нужно ли инициализировать переменную непосредственно перед тем, как передать ей значение, предоставленное пользователем, через другой источник (например, std::cin ), поскольку значение, предоставленное пользователем, просто перезапишет значение инициализации. В соответствии с нашей предыдущей рекомендацией о том, что переменные всегда следует инициализировать, лучше всего сначала инициализировать переменную.

Мы обсудим, как std::cin обрабатывает недопустимые входные данные в следующем уроке (7.16 – std::cin и обработка недопустимых входных данных).

Для продвинутых читателей

Резюме

Подробнее об операторах мы поговорим в уроке «1.9 – Знакомство с литералами и операторами».

Небольшой тест

Вопрос 1

Рассмотрим следующую программу, которую мы использовали выше:

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

б) Число с дробной частью. Попробуйте числа с дробными составляющими меньше 0,5 и больше 0,5 (например, 3,2 и 3,7).

Дробная составляющая опущена.

Всё прекрасно работает.

г) Слово, например «Hello».

д) Действительно большое число (минимум 3 миллиарда).

Вы получаете, казалось бы, случайное число.

Последнее предложение может быть особенно неожиданным. Попробуйте! Это происходит потому, что x может содержать числа только до определенного размера. После этого он «переполняется». Мы обсудим переполнение в следующем уроке.

Источник

Стандартные библиотеки C ++ предоставляют расширенный набор возможностей ввода / вывода, которые мы увидим в последующих главах. В этой главе будут рассмотрены основные и наиболее распространенные операции ввода-вывода, необходимые для программирования на C ++.

Файлы заголовков библиотеки ввода-вывода

Этот файл объявляет службы для обработки файлов, управляемых пользователем. Мы обсудим это подробно в главе «Файл и поток».

Стандартный выходной поток (cout)

Оператор ввода может использоваться более одного раза в одном выражении, как показано выше, и endl используется для добавления новой строки в конце строки.

Стандартный входной поток (cin)

Компилятор C ++ также определяет тип данных введенного значения и выбирает соответствующий оператор извлечения потока для извлечения значения и сохранения его в данных переменных.

Оператор извлечения потока >> может использоваться более одного раза в одном выражении. Чтобы запросить более одного элемента данных, вы можете использовать следующее:

Это будет эквивалентно следующим двум утверждениям:

Стандартный поток ошибок (cerr)

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

Стандартный поток журнала (засорение)

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

Вы не сможете увидеть какие-либо различия в cout, cerr и clog с этими небольшими примерами, но при написании и выполнении больших программ разница становится очевидной. Поэтому хорошей практикой является отображение сообщений об ошибках с использованием потока cerr и при отображении других сообщений журнала, которые следует использовать.

Источник

20. Библиотека iostream

Частью стандартной библиотеки C++ является библиотека iostream – объектно-ориентированная иерархия классов, где используется и множественное, и виртуальное наследование. В ней реализована поддержка для файлового ввода/вывода данных встроенных типов. Кроме того, разработчики классов могут расширять эту библиотеку для чтения и записи новых типов данных.

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

Вывод осуществляется, как правило, с помощью перегруженного оператора сдвига влево ( >):

Назначение операторов легче запомнить, если считать, что каждый «указывает» в сторону перемещения данных. Например,

перемещает данные в x, а

перемещает данные из x. (В разделе 20.1 мы покажем, как библиотека iostream поддерживает ввод данных, а в разделе 20.5 – как расширить ее для ввода данных новых типов. Аналогично раздел 20.2 посвящен поддержке вывода, а раздел 20.4 – расширению для вывода данных определенных пользователем типов.)

Чтобы использовать часть библиотеки iostream, связанную с файловым вводом/выводом, необходимо включить в программу заголовочный файл

(Файл fstream уже включает iostream, так что включать оба файла необязательно.) Файловый ввод/вывод поддерживается теми же операторами:

Вот пример сеанса работы с этой программой. Нас просят ввести файл для сортировки. Мы набираем alice_emma (набранные на клавиатуре символы напечатаны полужирным шрифтом). Затем программа направляет на стандартный вывод все, что прочитала из файла:

Введите имя файла для сортировки: alice_emma

Далее программа выводит в файл outfile отсортированную последовательность строк. Конечно, на порядок слов влияют знаки препинания; в следующем разделе мы это исправим:

(В разделе 20.6 мы познакомимся с файловым вводом/выводом более подробно.)

Для использования любого из этих классов в программу нужно включить заголовочный файл

(Файл sstream уже включает iostream, так что включать оба файла необязательно.) В следующем фрагменте объект класса ostringstream используется для форматирования сообщения об ошибке, которое возвращается вызывающей программе.

(В разделе 20.8 мы познакомимся со строковым вводом/выводом более подробно.)

Потоки ввода/вывода поддерживают два предопределенных типа: char и wchar_t. В этой главе мы расскажем только о чтении и записи в потоки данных типа char. Помимо них, в библиотеке iostream имеется набор классов и объектов для работы с типом wchar_t. Они отличаются от соответствующих классов, использующих тип char, наличием префикса ‘w’. Так, объект стандартного ввода называется wcin, стандартного вывода – wcout, стандартного вывода для ошибок – wcerr. Но набор заголовочных файлов для char и wchar_t один и тот же.

Классы для ввода/вывода данных типа wchar_t называются wostream, wistream, wiostream, для файлового ввода/вывода – wofstream, wifstream, wfstream, а для строкового – wostringstream, wistringstream, wstringstream.

20.1. Оператор вывода

Оператор вывода обычно применяется для записи на стандартный вывод cout. Например, программа

печатает на терминале строку:

сплетница Анна Ливия

Имеются операторы, принимающие аргументы любого встроенного типа данных, включая const char*, а также типов string и complex из стандартной библиотеки. Любое выражение, включая вызов функции, может быть аргументом оператора вывода при условии, что результатом его вычисления будет тип, принимаемый каким-либо вариантом этого оператора. Например, программа

выводит на терминал следующее:

Длина ‘Улисс’ равна:7

Размер ‘Улисс’ равен:8

endl – это манипулятор вывода, который вставляет в выходной поток символ перехода на новую строку, а затем сбрасывает буфер объекта ostream. (С буферизацией мы познакомимся в разделе 20.9.)

Операторы вывода, как правило, удобнее сцеплять в одну инструкцию. Например, предыдущую программу можно записать таким образом:

Сцепление операторов вывода (и ввода тоже) возможно потому, что результатом выражения

служит левый операнд оператора вывода, т.е. сам объект cout. Затем этот же объект передается следующему оператору и далее по цепочке (мы говорим, что оператор >). Например, в следующей программе из стандартного ввода читается последовательность значений типа int и помещается в вектор:

читает целое число из стандартного ввода и копирует его в переменную ival. Результатом является левый операнд – объект класса istream, в данном случае cin. (Как мы увидим, это позволяет сцеплять операторы ввода.)

читает последовательность значений, пока cin не станет равно false. Значение istream может быть равно false в двух случаях: достигнут конец файла (т.е. все значения из файла прочитаны успешно) или встретилось неверное значение, скажем 3.14159 (десятичная точка недопустима в целом числе), 1e-1 (буква e недопустима) или любой строковый литерал. Если вводится неверное значение, объект istream переводится в состояние ошибки и чтение прекращается. (В разделе 20.7 мы подробнее расскажем о таких состояниях.)

Есть набор предопределенных операторов ввода, принимающих аргументы любого встроенного типа, включая C-строки, а также стандартных библиотечных типов string и complex:

Вот пример выполнения этой программы:

Пожалуйста, введите item_number, item_name и price:

Введены значения: item# 10247 widget @$19.99

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

Пожалуйста, введите item_number, item_name и price:

Введены значения: item# 10247 widget @$19.99

При чтении ошибка iostream более вероятна, чем при записи. Если мы вводим такую последовательность:

закончится ошибкой ввода, поскольку BuzzLightyear не принадлежит типу int. При проверке объекта istream будет возвращено false, поскольку возникло состояние ошибки. Более устойчивая к ошибкам реализация выглядит так:

Хотя сцепление операторов ввода поддерживается, проверить корректность каждой отдельной операции нельзя, поэтому пользоваться таким приемом следует лишь тогда, когда ошибка невозможна. Наша программа теперь выглядит так:

составлена из девяти символов: ‘a’, ‘b’, ‘ ‘ (пробел), ‘c’, ‘\n’ (переход на новую строку), ‘d’, ‘\t’ (табуляция), ‘e’ и ‘\n’. Однако приведенная программа читает лишь пять букв:

И печатает следующее:

По умолчанию все пустые символы отбрасываются. Если нам нужны и они, например для сохранения формата входного текста или обработки пустых символов (скажем, для подсчета количества символов перехода на новую строку), то можно воспользоваться функцией-членом get() класса istream (обычно в паре с ней употребляется функция-член put() класса ostream; они будут рассмотрены ниже). Например:

Другая возможность сделать это – использовать манипулятор noskipws.

Каждая из двух данных последовательностей считается составленной из пяти строк, разделенных пробелами, если для чтения используются операторы ввода с типами const char* или string:

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

Вместо того чтобы читать из стандартного ввода по одному символу, можно воспользоваться потоковым итератором istream_iterator:

Пусть входом для этой программы будет файл istream_iter.C с исходным текстом. В системе UNIX мы можем перенаправить стандартный ввод на файл следующим образом (istream_iter – имя исполняемого файла программы):

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

(Потоковые итераторы ввода/вывода iostream рассматривались в разделе 12.4.)

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

20.2.1. Строковый ввод

Считывание можно производить как в C-строки, так и в объекты класса string. Мы рекомендуем пользоваться последними. Их главное преимущество – автоматическое управление памятью для хранения символов. Чтобы прочитать данные в C-строку, т.е. массив символов, необходимо сначала задать его размер, достаточный для хранения строки. Обычно мы читаем символы в буфер, затем выделяем из хипа ровно столько памяти, сколько нужно для хранения прочитанной строки, и копируем данные из буфера в эту память:

Работать с типом string значительно проще:

Рассмотрим операторы ввода в C-строки и в объекты класса string. В качестве входного текста по-прежнему будет использоваться рассказ об Алисе Эмме:

Поместим этот текст в файл alice_emma, а затем перенаправим на него стандартный вход программы. Позже, когда мы познакомимся с файловым вводом, мы откроем и прочтем этот файл непосредственно. Следующая программа помещает прочитанные со стандартного ввода слова в C-строку и находит самое длинное слово:

После компиляции и запуска программа выводит следующие сведения:

Число прочитанных слов 65

Длина самого длинного слова 10

Самое длинное слово creature,»

На самом деле этот результат неправилен: самое длинное слово beautiful, в нем девять букв. Однако выбрано creature, потому что программа сочла его частью запятую и кавычку. Следовательно, необходимо отфильтровать небуквенные символы.

Но прежде чем заняться этим, рассмотрим программу внимательнее. В ней каждое слово помещается в массив buf, длина которого равна 24. Если бы в тексте попалось слово длиной 24 символа (или более), то буфер переполнился бы и программа, вероятно, закончилась бы крахом. Чтобы предотвратить переполнение входного массива, можно воспользоваться манипулятором setw(). Модифицируем предыдущую программу:

Завершается такая частичная строка двоичным нулем. Для использования setw() в программу необходимо включить заголовочный файл iomanip:

Если в объявлении массива buf размер явно не указан:

то программист может применить оператор sizeof, но при условии, что идентификатор является именем массива и находится в области видимости выражения:

Применение оператора sizeof в следующем примере дает неожиданный результат:

Функции setw() вместо размера массива передается размер указателя, длина которого на нашей машине равна четырем байтам, поэтому вывод разбит на строки по три символа.

Попытка исправить ошибку приводит к еще более серьезной проблеме:

Мы хотели передать setw() размер массива, адресуемого pbuf. Но выражение

дает только один символ, т.е. объект типа char. Поэтому setw() передается значение 1. На каждой итерации цикла while в массив, на который указывает pbuf, помещается только нулевой символ. До чтения из стандартного ввода дело так и не доходит, программа зацикливается.

При использовании класса string все проблемы управления памятью исчезают, об этом заботится сам string. Вот как выглядит наша программа в данном случае:

Однако запятая и кавычка по-прежнему считаются частью слова. Напишем функцию для удаления этих символов из слова:

Эта функция работает правильно, но множество символов, которые мы собираемся отбрасывать, «зашито» в код. Лучше дать пользователю возможность самому передать строку, содержащую такие символы. Если он согласен на множество по умолчанию, то может передать пустую строку.

Более общая версия filter_string() принимает пару итераторов, обозначающих диапазон, где производится фильтрация:

С использованием этой функции программа будет выглядеть так:

Когда мы применили в алгоритме max_element() стандартный оператор «меньше», определенный в классе string, то были удивлены полученным результатом:

Число прочитанных слов 65

Длина самого длинного слова 4

Самое длинное слово wind

Очевидно, что wind – это не самое длинное слово. Оказывается, оператор «меньше» в классе string сравнивает строки не по длине, а в лексикографическом порядке. И в этом смысле wind – действительно максимальный элемент. Для того чтобы найти слово максимальной длины, мы должны заменить оператор «меньше» предикатом length_less(). Тогда результат будет таким:

Число прочитанных слов 65

Длина самого длинного слова 9

Самое длинное слово beautiful

Прочитайте из стандартного ввода последовательность данных таких типов: string, double, string, int, string. Каждый раз проверяйте, не было ли ошибки чтения.

Прочитайте из стандартного ввода заранее неизвестное число строк. Поместите их в список. Найдите самую длинную и самую короткую строку.

20.3. Дополнительные операторы ввода/вывода

Иногда необходимо прочитать из входного потока последовательность не интерпретируемых байтов, а типов данных, таких, как char, int, string и т.д. Функция-член get() класса istream читает по одному байту, а функция getline() читает строку, завершающуюся либо символом перехода на новую строку, либо каким-то иным символом, определяемым пользователем. У функции-члена get() есть три формы:

1. get(char& ch) читает из входного потока один символ (в том числе и пустой) и помещает его в ch. Она возвращает объект iostream, для которого была вызвана. Например, следующая программа собирает статистику о входном потоке, а затем копирует входной поток в выходной:

Функция-член put() класса ostream дает альтернативный метод вывода символа в выходной поток: put() принимает аргумент типа char и возвращает объект класса ostream, для которого была вызвана.

После компиляции и запуска программа печатает следующий результат:

При использовании любой из этих форм get() для чтения данной последовательности нужно семь итераций:

get(char *sink, streamsize size, char delimiter=’\n’)

sink – это массив, в который помещаются символы. size – это максимальное число символов, читаемых из потока istream. delimiter – это символ-ограничитель, при обнаружении которого чтение прекращается. Сам ограничитель не читается, а оставляется в потоке и будет прочитан следующим. Программисты часто забывают удалить его из потока перед вторым обращением к get(). Чтобы избежать этой ошибки, в показанной ниже программе мы воспользовались функцией-членом ignore() класса istream. По умолчанию ограничителем является символ новой строки.

Эта форма get() возвращает объект istream, для которого была вызвана (функция-член gcount() позволяет узнать количество прочитанных символов). Вот простой пример ее применения:

Если на вход этой программы подать текст о юной Алисе Эмме, то результат будет выглядеть так:

фактически прочитано символов: 52

фактически прочитано символов: 60

фактически прочитано символов: 66

фактически прочитано символов: 63

фактически прочитано символов: 61

фактически прочитано символов: 43

Чтобы еще раз протестировать поведение программы, мы создали строку, содержащую больше max_line символов, и поместили ее в начало текста. Получили:

фактически прочитано символов: 1023

фактически прочитано символов: 528

фактически прочитано символов: 52

фактически прочитано символов: 60

фактически прочитано символов: 66

фактически прочитано символов: 63

фактически прочитано символов: 61

фактически прочитано символов: 43

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

ignore() читает и отбрасывает length символов из потока или все символы до ограничителя включительно или до конца файла и возвращает объект istream, для которого вызвана.

Мы рекомендуем пользоваться функцией getline(), а не get(), поскольку она автоматически удаляет ограничитель из потока. Сигнатура getline() такая же, как у get() с тремя аргументами (и возвращает она тоже объект istream, для которого вызвана):

Поскольку и getline(), и get() с тремя аргументами могут читать size символов или меньше, то часто нужно “спросить” у объекта istream, сколько символов было фактически прочитано. Это позволяет сделать функция-член gcount(): она возвращает число символов, прочитанных при последнем обращении к get() или getline().

Функция-член write() класса ostream дает альтернативный метод вывода массива символов. Вместо того чтобы выводить символы до завершающего нуля, она выводит указанное число символов, включая и внутренние нули, если таковые имеются. Вот ее сигнатура:

Здесь length определяет, сколько символов выводить. write() возвращает объект класса ostream, для которого она вызвана.

Парной для функции write() из класса ostream является функция read() из класса istream с такой сигнатурой:

read() читает size соседних байт из входного потока и помещает их, начиная с адреса addr. Функция gcount() возвращает число байт, прочитанных при последнем обращении к read(). В свою очередь read() возвращает объект класса istream, для которого она вызвана. Вот пример использования getline(), gcount() и write():

Когда на вход было подано несколько фраз из романа Германа Мелвилла «Моби Дик», программа напечатала следующее:

Строка #1 Прочитано символов: 45

Строка #2 Прочитано символов: 46

Строка #3 Прочитано символов: 48

Строка #4 Прочитано символов: 51

Строка #5 Прочитано символов: 47

Строка #6 Прочитано символов: 43

Строка #7 Прочитано символов: 28

Всего прочитано строк: 7

Самая длинная строка: 51

Функция-член getline() класса istream поддерживает только ввод в массив символов. Однако в стандартной библиотеке есть обычная функция getline(), которая помещает символы в объект класса string:

Эта функция читает не более str::max_size()-1 символов. Если входная последовательность длиннее, то операция завершается неудачно и объект переводится в ошибочное состояние. В противном случае ввод прекращается, когда прочитан ограничитель (он удаляется из потока, но в строку не помещается) либо достигнут конец файла.

Вот еще три необходимые нам функции-члена класса istream:

Следующий фрагмент иллюстрирует использование некоторых из них:

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

Прочитайте фразу «riverrun, from bend of bay to swerve of shore» сначала как последовательность из девяти строк, а затем как одну строку.

С помощью функций getline() и gcount() прочитайте последовательность строк из стандартного ввода и найдите самую длинную (не забудьте, что строку, прочитанную за несколько обращений к getline(), нужно считать одной).

20.4. Перегрузка оператора вывода

Если мы хотим, чтобы наш тип класса поддерживал операции ввода/вывода, то необходимо перегрузить оба соответствующих оператора. В этом разделе мы рассмотрим, как перегружается оператор вывода. (Перегрузка оператора ввода – тема следующего раздела.) Например, для класса WordCount он выглядит так:

Проектировщик должен решить, следует ли выводить завершающий символ новой строки. Лучше этого не делать: поскольку операторы вывода для встроенных типов такой символ не печатают, пользователь ожидает аналогичного поведения и от операторов в других классах. Определенный нами в классе WordCount оператор вывода можно использовать вместе с любыми другими операторами:

Программа печатает на терминале строки:

Оператор вывода – это бинарный оператор, который возвращает ссылку на объект класса ostream. В общем случае структура определения перегруженного оператора вывода выглядит так:

Первый его аргумент – это ссылка на объект ostream, а второй – ссылка (обычно константная) на объект некоторого класса. Возвращается ссылка на ostream. Значением всегда является объект ostream, для которого оператор вызывался.

Поскольку первым аргументом является ссылка, оператор вывода должен быть определен как обычная функция, а не член класса. (Объяснение см. в разделе 15.1.) Если оператору необходим доступ к неоткрытым членам, то следует объявить его другом класса. (О друзьях говорилось в разделе 15.2.)

Пусть Location – это класс, в котором хранятся номера строки и колонки вхождения слова. Вот его определение:

Изменим определение класса WordCount, включив в него вектор occurList объектов Location и объект word класса string:

В классах string и Location определен оператор вывода operator >) похожа на перегрузку оператора вывода, но, к сожалению, возможностей для ошибок гораздо больше. Вот, например, его реализация для класса WordCount:

Инструкция зациклится, если объект istream будет находиться в ошибочном состоянии. Поэтому перед каждым обращением к get() проверяется отсутствие ошибки:

Если объект istream не в «хорошем» состоянии, то его значение будет равно false. (О состояниях потока мы расскажем в разделе 20.7.)

Данная программа считывает объект класса WordCount, сохраненный оператором вывода из предыдущего раздела:

Оператор ввода класса WordCount сам читает объекты класса Location. Вынесите этот код в отдельный оператор ввода класса Location.

Реализуйте оператор ввода для класса Date из упражнения 20.7 в разделе 20.4.

Реализуйте оператор ввода для класса CheckoutRecord из упражнения 20.8 в разделе 20.4.

20.6. Файловый ввод/вывод

Если программе необходимо работать с файлом, то следует включить в нее заголовочный файл fstream (который в свою очередь включает iostream):

Если файл будет использоваться только для вывода, мы определяем объект класса ofstream. Например:

Передаваемые конструктору аргументы задают имя открываемого файла и режим открытия. Файл типа ofstream может быть открыт либо – по умолчанию – в режиме вывода (ios_base::out), либо в режиме дозаписи (ios_base::app). Такое определение файла outfile2 эквивалентно приведенному выше:

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

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

Класс ofstream является производным от ostream. Все определенные в ostream операции применимы и к ofstream. Например, инструкции

выводят в файл outFile последовательность символов:

Следующая программа читает из стандартного ввода символы и копирует их в стандартный вывод:

К объекту класса ofstream можно применять и определенные пользователем экземпляры оператора вывода. Данная программа вызывает оператор вывода класса WordCount из предыдущего раздела:

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

Программа, показанная ниже, читает наш текстовый файл alice_emma, фильтрует его с помощью функции filter_string() (см. раздел 20.2.1, где приведены текст этой функции и содержимое файла), сортирует строки, удаляет дубликаты и записывает результат на стандартный вывод:

После компиляции и запуска программа выводит следующее:

Объекты классов ofstream и ifstream разрешено определять и без указания имени файла. Позже к этому объекту можно присоединить файл с помощью функции-члена open():

бъект класса fstream (производного от iostream) может открывать файл для ввода или вывода. В следующем примере файл word.out сначала считывается, а затем записывается с помощью объекта типа fstream. Созданный ранее в этом разделе файл word.out содержит объект WordCount:

Объект класса fstream может также открывать файл одновременно для ввода и вывода. Например, приведенная инструкция открывает файл word.out для ввода и дозаписи:

Для задания нескольких режимов используется оператор побитового ИЛИ. Объект класса fstream можно позиционировать с помощью функций-членов seekg() или seekp(). Здесь буква g обозначает позиционирование для чтения (getting) символов (используется с объектом класса ofstream), а p – для записи (putting) символов (используется с объектом класса ifstream). Эти функции делают текущим тот байт в файле, который имеет указанное абсолютное или относительное смещение. У них есть два варианта:

В первом варианте текущая позиция устанавливается в некоторое абсолютное значение, заданное аргументом current_position, причем значение 0 соответствует началу файла. Например, если файл содержит такую последовательность символов:

В следующем примере каждый вызов seekg() позиционирует файл на i-ую запись:

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

Текущая позиция чтения в файле типа fstream возвращается любой из двух функций-членов tellg() или tellp(). Здесь ‘p’ означает запись (putting) и используется с объектом ofstream, а ‘g’ говорит о чтении (getting) и обслуживает объект ifstream:

Если необходимо сместиться вперед от текущей позиции на одну запись типа Record, то можно воспользоваться любой из данных инструкций:

Разберем реальный пример. Дан текстовый файл, нужно вычислить его длину в байтах и сохранить ее в конце файла. Кроме того, каждый раз при встрече символа новой строки требуется сохранить текущее смещение в конце файла. Вот наш текстовый файл:

Программа должна создать файл, модифицированный следующим образом:

Так выглядит первая попытка реализации:

inOut – это объект класса fstream, связанный с файлом copy.out, открытым для ввода и дозаписи. Если файл открыт в режиме дозаписи, то все новые данные записываются в конец.

При чтении любого (включая пробельные) символа, кроме конца файла, мы увеличиваем переменную cnt на 1 и копируем прочитанный символ на терминал, чтобы вовремя заметить ошибки в работе программы.

Встретив символ новой строки, мы записываем текущее значение cnt в inOut. Как только будет достигнут конец файла, цикл прекращается. Окончательное значение cnt выводится в файл и на экран.

Программа компилируется без ошибок и кажется правильной. Но если подать на вход несколько фраз из романа «Моби Дик» Германа Мелвилла:

то получим такой результат:

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

мы читаем конец файла, цикл while завершается и выводится значение 0.

Хотя мы допустили серьезную ошибку, исправить ее совсем несложно, поскольку причина понятна. Надо лишь перед чтением переустановить файл на начало. Это делается с помощью обращения:

Запустим программу заново. На этот раз она печатает:

Как видим, выводится лишь первая строка текста и счетчик для нее, а оставшиеся шесть строк проигнорированы. Ну что ж, исправление ошибок – неотъемлемая часть профессии программиста. А проблема опять в том, что файл открыт в режиме дозаписи. Как только мы в первый раз вывели cnt, файл оказался позиционирован на конец. При следующем обращении к get() читается конец файла, и цикл while снова завершается преждевременно.

Нам необходимо встать на ту позицию в файле, где мы были перед выводом cnt. Для этого понадобятся еще две инструкции:

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

Дело в том, что inOut находится в состоянии «конец файла», в котором операции ввода и вывода не выполняются. Для решения проблемы необходимо сбросить это состояние с помощью функции-члена clear():

Окончательный вариант программы выглядит так:

Вот теперь – наконец-то! – все правильно. При реализации этой программы было необходимо явно сформулировать поведение, которое мы собирались поддержать. А каждое наше исправление было реакцией на выявившуюся ошибку вместо анализа проблемы в целом.

Пользуясь операторами вывода для класса Date, которые вы определили в упражнении 20.7, или для класса CheckoutRecord из упражнения 20.8 (см. раздел 20.4), напишите программу, позволяющую создать файл и писать в него.

Напишите программу для открытия и чтения файла, созданного в упражнении 20.12. Выведите содержимое файла на стандартный вывод.

Напишите программу для открытия файла, созданного в упражнении 20.12, для чтения и дозаписи. Выведите экземпляр класса Date или CheckoutRecord:

20.7. Состояния потока

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

и вводим слово «Borges», то cin переводится в состояние ошибки после неудачной попытки присвоить строковый литерал целому числу. Если бы мы ввели число 1024, то чтение прошло бы успешно и поток остался бы в нормальном состоянии.

Чтобы выяснить, в каком состоянии находится поток, достаточно проверить его значение на истину:

Для чтения заранее неизвестного количества элементов мы обычно пишем цикл while:

Условие в цикле while будет равно false, если достигнут конец файла или произошла ошибка при чтении. В большинстве случаев такой проверки потокового объекта достаточно. Однако при реализации оператора ввода для класса WordCount из раздела 20.5 нам понадобился более точный анализ состояния.

Существует два способа явно изменить состояние потока iostream. С помощью функции-члена clear() ему явно присваивается указанное значение. Функция setstate() не сбрасывает состояние, а устанавливает один из флагов, не меняя значения остальных. Например, в коде оператора ввода для класса WordCount при обнаружении неверного формата мы используем setstate() для установки флага fail в состоянии объекта istream:

Имеются следующие значения флагов состояния:

Для установки сразу нескольких флагов используется побитовый оператор ИЛИ:

При тестировании оператора ввода в классе WordCount (см. раздел 20.5) мы писали:

Возможно, вместо этого мы предпочли бы продолжить выполнение программы, предупредив пользователя об ошибке и попросив повторить ввод. Но перед чтением нового значения из потока cin необходимо перевести его в нормальное состояние. Это можно сделать с помощью функции-члена clear():

В более общем случае clear() используется для сброса текущего состояния и установки одного или нескольких флагов нового. Например:

восстанавливает нормальное состояние потока. (Оба вызова эквивалентны, поскольку goodbit является для clear() аргументом по умолчанию.)

Функция-член rdstate() позволяет получить текущее состояние объекта:

Измените один (или оба) оператор ввода для класса Date из упражнения 20.7 и/или класса CheckoutRecord из упражнения 20.8 (см. раздел 20.4) так, чтобы они устанавливали состояние объекта istream. Модифицируйте программы, которыми вы пользовались для тестирования этих операторов, для проверки явно установленного состояния, вывода его на печать и сброса в нормальное. Протестируйте программы, подав на вход правильные и неправильные данные.

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

Библиотека iostream поддерживает операции над строковыми объектами в памяти. Класс ostringstream вставляет символы в строку, istringstream читает символы из строкового объекта, а stringstream может использоваться как для чтения, так и для записи. Чтобы работать со строковым потоком, в программу необходимо включить заголовочный файл Например, следующая функция читает весь файл alice_emma в объект buf класса ostringstream. Размер buf увеличивается по мере необходимости, чтобы вместить все символы: #include #include #include string read_file_into_string()

Функция-член str() возвращает строку – объект класса string, ассоциированный со строковым потоком ostringstream. Этой строкой можно манипулировать так же, как и “обычным” объектом класса string. Например, в следующей программе text почленно инициализируется строкой, ассоциированной с buf:

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

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

Приложение может сохранить такие строки для последующего отображения и даже рассортировать их по серьезности. Обобщить эту идею помогают классы Notify (извещение), Log (протокол) и Error (ошибка).

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

В языке Си форматирование выходного сообщения производится с помощью функций семейства printf(). Например, следующий фрагмент

Первым аргументом printf() является форматная строка. Каждый символ % показывает, что вместо него должно быть подставлено значение аргумента, а следующий за ним символ определяет тип этого аргумента. Вот некоторые из поддерживаемых типов (полное описание см. в [KERNIGHAN88]):

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

20.9. Состояние формата

Каждый объект класса из библиотеки iostream поддерживает состояние формата, которое управляет выполнением операций форматирования, например основание системы счисления для целых значений или точность для значений с плавающей точкой. Для модификации состояния формата объекта в распоряжении программиста имеется предопределенный набор манипуляторов.1 Манипулятор применяется к потоковому объекту так же, как к данным. Однако вместо чтения или записи данных манипулятор модифицирует внутреннее состояние потока. Например, по умолчанию объект типа bool, имеющий значение true (а также литеральная константа true), выводится как целая ‘1’:

Чтобы поток cout выводил переменную illustrate в виде слова true, мы применяем манипулятор boolalpha:

Поскольку манипулятор возвращает потоковый объект, к которому он применялся, то допустимо прицеплять его к выводимым данным и другим манипуляторам. Вот как можно перемежать данные и манипуляторы в нашей программе:

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

Чтобы отменить сделанную модификацию потока cout, необходимо использовать манипулятор noboolalpha:

Как мы покажем, для многих манипуляторов имеются парные.

По умолчанию значения арифметических типов читаются и записываются в десятичной системе счисления. Программист может изменить ее на восьмеричную или шестнадцатеричную, а затем вернуться к десятичной (это распространяется только на целые типы, но не на типы с плавающей точкой), пользуясь манипуляторами hex, oct и dec:

Эта программа печатает следующее:

Вот та же программа, но и с использованием showbase:

Манипулятор noshowbase восстанавливает состояние cout, при котором основание системы счисления не выводится.

По умолчанию значения с плавающей точкой выводятся с точностью 6. Эту величину можно модифицировать с помощью функции-члена precision(int) или манипулятора setprecision(); для использования последнего необходимо включить заголовочный файл iomanip. precision() возвращает текущее значение точности. Например:

После компиляции и запуска программа печатает следующее:

Манипуляторы, принимающие аргумент, такие, как setprecision() и setw(), требуют включения заголовочного файла iomanip:

Кроме описанных аспектов, setprecision() имеет еще два: на целые значения он не оказывает никакого влияния; значения с плавающей точкой округляются, а не обрезаются. Таким образом, при точности 4 значение 3.14159 печатается как 3.142, а при точности 3 – как 3.14.

По умолчанию десятичная точка не печатается, если дробная часть значения равна 0. Например:

Чтобы точка выводилась, воспользуйтесь манипулятором showpoint:

Манипулятор noshowpoint восстанавливает поведение по умолчанию.

По умолчанию значения с плавающей точкой выводятся в нотации с фиксированной точкой. Для перехода на научную нотацию используется идентификатор scientific, а для возврата к прежней нотации – модификатор fixed:

В результате печатается:

с фиксированной точкой: 10

Если бы мы захотели вместо буквы ‘e’ выводить ‘E’, то следовало бы употребить манипулятор uppercase, а для возврата к ‘e’ – nouppercase. (Манипулятор uppercase не приводит к переводу букв в верхний регистр при печати.)

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

читает все буквы от ‘a’ до ‘d’ за четыре итерации, а пробельные разделители оператором ввода игнорируются. Манипулятор noskipws отменяет такой пропуск пробельных символов:

Теперь цикл while будет выполняться семь раз. Чтобы восстановить поведение по умолчанию, к потоку cin применяется манипулятор skipws.

приводит к сбросу буфера cout.

В любой момент времени объект ostream разрешено связывать только с одним объектом istream. Чтобы разорвать существующую связь, мы передаем функции-члену tie() значение 0:

Мы можем управлять шириной поля, отведенного для печати числового или строкового значения, с помощью манипулятора setw(). Например, программа

Второй модификатор setw() необходим потому, что, в отличие от других манипуляторов, setw() не изменяет состояние формата объекта ostream.

Чтобы выровнять значение по левой границе, мы применяем манипулятор left (соответственно манипулятор right восстанавливает выравнивание по правой границе). Если мы хотим получить такой результат:

то пользуемся манипулятором internal, который выравнивает знак по левой границе, а значение – по правой, заполняя пустое пространство пробелами. Если же нужен другой символ, то можно применить манипулятор setfill(). Так

В табл. 20.1 приведен полный перечень предопределенных манипуляторов.

Таблица 20.1. Манипуляторы

Представлять true и false в виде строк

Представлять true и false как 1 и 0

Печатать префикс, обозначающий систему счисления

Не печатать префикс система: счисления

Всегда печатать десятичную точку

Печатать десятичную точку только в том случае, если дробная часть ненулевая

Печатать + для неотрицательных чисел

Не печатать + для неотрицательных чисел

Пропускать пробельные символы в операторах ввода

Не пропускать пробельные символы в операторах ввода

Печатать в десятичной системе

Печатать в шестнадцатеричной системе

Печатать в восьмеричной системе

Добавлять символ заполнения справа от значения

Добавлять символ заполнения слева от значения

Добавлять символ заполнения между знаком и значением

Отображать число с плавающей точкой в десятичной нотации

Отображать число с плавающей точкой в научной нотации

Сбросить буфер ostream

Вставить нулевой символ, затем сбросить буфер ostream

Вставить символ новой строки, затем сбросить буфер ostream

Пропускать пробельные символы

// для этих манипуляторов требуется #include

Заполнять пустое место символом ch

Установить точность вывода числа с плавающей точкой равной n

Установить ширину ноля ввода или вывода равной w

Выводить целые числа по основанию b

* обозначает состояние потока но умолчанию

20.10. Сильно типизированная библиотека

Библиотека iostream сильно типизирована. Например, попытка прочитать из объекта класса ostream или записать в объект класса istream помечается компилятором как

нарушение типизации. Так, если имеется набор объявлений:

то следующие две инструкции приводят к нарушению типизации, обнаруживаемому во время компиляции:

Средства ввода/вывода включены в состав стандартной библиотеки C++. В главе 20 библиотека iostream описана не полностью, в частности вопрос о создании определенных пользователем манипуляторов и буферных классов остался за рамками введения в язык. Мы сосредоточили внимание лишь на той части библиотеки iostream, которая имеет основополагающее значение для программного ввода/вывода.
Назад Вперед

на странице http://valera.asf.ru/cpp/book/c20.shtml текст оканчивается словами «. cout

Спасибо, не заметил. Поправил.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

МанипуляторНазначение