О некоторых методах класса istream в языке программирования C++

NovaInfo 57, с.63-69, скачать PDF
Опубликовано
Раздел: Технические науки
Язык: Русский
Просмотров за месяц: 67
CC BY-NC

Аннотация

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

Ключевые слова

ЯЗЫК ПРОГРАММИРОВАНИЯ С++, КЛАСС ISTREAM, ПОТОКИ ВВОДА-ВЫВОДА

Текст научной работы

Класс istream определяет оператор >> ("прочесть из") для организации ввода встроенных типов. По умолчанию операция >> используется также в качестве битовой операции сдвига, однако это не имеет отношения к вводу: класс istream переопределяет операцию >> для организации ввода, причем эта операция перегружена и может применяться со всеми базовыми типами C++. Класс istream вводит оператор >> для каждого из этих типов данных, причем оператор >> возвращает ссылку на объект istream. Поскольку cin сам является объектом istream, то результат выполнения одной операции ввода может быть началом следующей операции ввода, и может быть организован сцепленный ввод данных:

cin>>x>>y>>z;

Для ввода типов, определенных пользователем, оператор >> необходимо перегрузить соответствующим образом [1-4]. После этого перегруженным оператором >> можно будет пользоваться точно так же, как и оператором >> для встроенных типов.

С объектом cin можно использовать манипуляторы hex, dec и oct для указания того, что вводимое целое число должно быть интерпретировано в соответствующем формате. Например, строка

cin>>hex>>a>>b;

позволяет при вводе переменных a и b вводимые значения воспринимать как шестнадцатеричные. Поэтому, если для значений переменных при вводе указать ff и 10, то строка

cout<<a<<"  "<<b;

выведет в качестве результата 255 и 16.

Поскольку оператор >> пропускает символы-разделители, можно считать, например, последовательность целых чисел, разделенных пробелами. Так, значения переменным x, y, z в выражении

cin>>x>>y>>z;

можно задать в виде строки

10  134  16

Кроме перегружаемого оператора >>, объект cin имеет достаточно большое количество встроенных методов. Рассмотрим некоторые их них.

Максимальное количество символов, считываемых оператором >>, можно задать функцией width():

char x[5];
cin.width(3); cin>>x;
cout<<"x= "<<x;

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

Оператор >> предназначен для форматированного ввода (считывания данных ожидаемого формата), однако в некоторых случаях требуется считывать именно символы, а уже затем проверять их. В таких случаях можно использовать функции-члены get(), которые называют функциями неформатированного ввода – они читают символьный ввод как есть, без пропуска пробелов и без преобразований данных.

Для ввода одного символа применяются методы get(char &) и get(void) (или просто get()). Первый вариант функции присваивает входящий символ своему аргументу, а второй вариант – просто преобразует его в целочисленный тип и возвращает это значение.

Функция get() без параметров используется достаточно редко, ее нельзя использовать для последовательной инициализации нескольких переменных, т.к. возвращаемое функцией значение не является объектом iostream (функция возвращает в программу значение типа int). Ее можно использовать, например следующим образом:

int i = 0; char ch;
while ((ch = cin.get()) != '\n')
{ cout<<ch; i++; }
cout<<"Было введено "<<i<<" символов.";

В этом фрагменте программы последовательность символов вводится сначала в буфер ввода, и если в нем встречается символ "\n", то ввод заканчивается, и только потом содержимое буфера выводится на экран, а также выводится сообщение о количестве введенных символов. Поэтому, если содержимое цикла изменить на

{ cout<<ch; i++; cout<<"##";}

и при вводе ввести строку:

12345 xyz

то после нажатия на клавишу "Enter" на экране мы увидим

1##2##3##4##5## ##x##y##z##

Если использовать функцию get(void) для ввода из файла, то при достижении конца файла (в том числе эмулированного c клавиатуры по нажатию клавиш "Ctrl" + "Z"), функция возвратит значение EOF, являющееся символической константой. Это позволяет организовать ввод следующим образом:

int ch;
while((ch = cin.get()) != EOF)
{ // какие-либо действия по обработке ввода }

Обращаю внимание на тот факт, что в данном случае необходимо использовать тип int, а не char для переменной ch, т.к. значение EOF не может быть выражено типом char.

Рассмотрим теперь функцию-член get(char &). Она возвращает ссылку на объект istream, использованный для ее вызова, поэтому можно осуществить сцепленный ввод значений нескольким переменным:

char ch1, ch2, ch3, ch4;
cin.get(ch1).get(ch2).get(ch3)>>ch4;

Если метод cin.get(char &) встречает признак конца файла, то он не присваивает значение своему аргументу; более того, при этом метод вызовет setstate(failbit), что приведет к тому, что cin вернет false. Все это позволяет осуществлять ввод следующим образом:

char ch;
while(cin.get(ch))
{ // какие-либо действия по обработке ввода }

В данном случае пока ввод корректен, цикл будет продолжать работу, т.к. возвращаемым значением cin.get(ch) в таком случае будет cin, оценивающийся как true. Если же будет достигнут конец файла, то возвращаемым значением будет false, и цикл прервется.

Существует еще один вариант перегруженной функции get(), принимающий три параметра. Первый параметр – указатель на массив символов, второй – указывает максимальное число символов в строке (нулевой символ строки также учитывается, он добавляется автоматически), третий параметр позволяет задать символ-разделитель. Третий параметр может быть опущен, и в таком случае будет использован параметр по умолчанию для символа-разделителя – "\n". Функция будет читать символы до тех пор, пока не будет прочтено их максимально указанное количество, или до тех пор, пока не встретится символ-разделитель (в зависимости от того, что наступит раньше). Типичное использование функции get() с тремя аргументами – считывание строки в буфер фиксированного размера с целью ее дальнейшего анализа. Например, следующий простой фрагмент кода осуществляет ввод в массив ch:

char ch[30];
cin.get(ch, 30);

Здесь функция cin.get() прекратит чтение ввода в массив после получения 29 символов, или после получения символа перевода строки ("\n").

Основное отличие функции get() с тремя параметрами от функции getline() в том, что get() оставляет символ перевода строки во входном потоке и делает его доступным для следующей операции ввода; getline() при этом отбрасывает символы новой строки из входного потока. Эту особенность функции get() нужно помнить, т.к. в противном случае это может привести к ошибкам в работе программы. Так, например, никогда нельзя обращаться к get() дважды, не удалив завершающий символ. В следующем фрагменте кода повторный вызов метода get() будет неудачен:

char ch[30]; int i=1;
while (cin)
{ cin.get(ch,10);
cout<<"Ввод "<<i++<<": "<<ch<<endl;}

Если в этом фрагменте кода cin.get(ch,10) заменить на cin.getline(ch,10), то код будет нормально работать. Данный пример показывает, что лучше использовать метод getline(), а не get().

В тех случаях, когда возникает необходимость пропустить часть символов строки, используется функция ignore(). Функция ignore(), как уже было сказано, получает два аргумента и возвращает тип istream &; она считывает символы, но нигде не хранит их. Так как функция возвращает вызывающий объект, то это позволяет выполнять конкатенацию (сцепление) вызовов функции:

cin.ignore(255, '\n').ignore(255, '\n');

Здесь два вызова функции ignore() позволяют считать и отбросить две строки.

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

Рассмотрим еще такие важные методы класса istream, как read(), peek(), gcount() и putback().

Функция read() позволяет считать заданное количество байт и сохранить их в указанном месте. Хотя данный метод и не предназначен для клавиатурного ввода (обычно он используется совместно с методом write() для файлового ввода и вывода), его можно использовать для считывания из стандартного потока ввода. Так, приведенный ниже фрагмент кода читает 15 символов с клавиатуры и помещает их в массив ch:

char ch[15];
cin.read(ch,15);

Метод read() не добавляет нулевой символ к вводу, поэтому он не преобразует ввод в строковую форму. Значит, при необходимости работать с введенными данными как со строкой, нужно будет указать символ конца строки вместо последнего элемента массива. Стоит также помнить, что нажатие клавиши "Enter" не прекращает ввод, и read() все равно считает указанное количество символов.

Функция peek() возвращает следующий символ ввода без извлечения его из входного потока. Поэтому ее можно использовать для просмотра этого символа потока, например, с целью выяснения, нужно ли выполнять какое-либо действие. В следующем фрагменте кода вызов cin.peek() берет следующий входной символ и присваивает его значение ch. После этого проверяется условие выхода из цикла. Внутри цикла читается символ в очередной элемент массива. По завершении цикла выводится введенная строка. Однако сам символ-признак выхода из цикла (в нашем случае "." или "!") остается во входном потоке.

char ch, s[25];
int i = 0;
while ((ch=cin.peek()) != '.' && ch != '!')
cin.get(s[i++]);
s[i]='\0';
cout<<"Введенная строка: "<<s<<endl;

Стоит, однако, отметить, что если количество введенных символов окажется больше, чем 24 (условие выхода из цикла окажется дальше 24-го символа), то это приведет к ошибке, т.к. массив символов s должен по описанию содержать не более 25 символов.

Опять-таки, в зависимости от используемого компилятора, возможны следующие варианты [1]. Компилятор Microsoft Visual C++ 2005 Express выделяет память для размещения элементов строки, кратную 16. Поэтому это позволит нормально разместить в массиве до 32 символов. Компиляторы Borland C++ 5.02 и wxDev-C++ 7.4 выделяют памяти ровно столько, сколько требуется, поэтому ошибка возникнет при превышении лимита в 25 символов.

Следующий пример показывает, как можно использовать функцию peek() для определения того, прочиталась ли вся строка целиком в переменную, или нет:

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{setlocale(LC_ALL,"rus");
  char country[8], city[10];
  cout<<"Страна: ";
  cin.get(country,8);
  if (cin.peek() != '\n')
             cout<<"Название страны сокращено до: "<<country;
  cout<<"\nГород: ";
  cin.sync();
  cin.get(city,10);
  if (cin.peek() != '\n')
             cout<<"Название города сокращено до: "<<city<<endl;
  system("pause"); return 0;}

Метод putback() вставляет символ в поток ввода. При этом вставленный символ становится первым символом, прочитанным следующим оператором ввода. Функция putback() принимает один аргумент типа char и возвращает тип istream &. Существенно, что putback() дает возможность вернуть в поток символ, отличающийся от последнего прочитанного. Так, в следующем фрагменте кода

char ch;
while ((ch=cin.get()) != '\n')
{ if (ch == '0') cin.putback('#');
cout<<ch; }

после каждого введенного символа "0" функция cin.putback('#') вернет в поток символ "#". Например, если ввести строку

12300321010203

то в результате на экране мы увидим

1230#0#3210#10#20#3

В следующем фрагменте кода символ "0" пропускается при выводе на экран (для окончания ввода нужно эмулировать признак конца файла комбинацией "Ctrl" + "Z"):

char ch;
while (cin.get(ch))
{ if (ch == '0') cin.putback('#'); else cout<<ch; }

Следующая функция-член gcount() возвращает количество символов, прочитанных из потока функцией неформатированного ввода в последний раз. Таким образом, сюда относятся символы, прочитанные методами get(), getline(), ignore(), read(). Пример использования gcount() для выяснения, того, сколько символов было прочитано из потока, показан ниже.

char s[25];
cin.getline(s,25);
cout<<"Считано символов: "<<cin.gcount();

Читайте также

Список литературы

  1. Дмитриев В.Л. Теория и практика программирования на С++. – Стерлитамак: РИО СФ БашГУ, 2013. – 308 с.
  2. Прата С. Язык программирования С++. Лекции и упражнения, 5-е изд.: Пер. с англ. – М.: Вильямс, 2007. – 1184 с.
  3. Страуструп Б. Язык программирования С++. Специальное издание. – М.: Бином, 2004. – 1054 с.
  4. Stroustrup Bjarne. The C++ programming language / Bjarne Stroustrup. – Fourth edition. – Boston: Addison-Wesley, 2013. – 1368 p.

Цитировать

Дмитриев, В.Л. О некоторых методах класса istream в языке программирования C++ / В.Л. Дмитриев. — Текст : электронный // NovaInfo, 2016. — № 57. — С. 63-69. — URL: https://novainfo.ru/article/9709 (дата обращения: 29.03.2023).

Поделиться