Язык программирования C++ от Страуструпа

       

Ввод встроенных типов


Класс istream определяется следующим образом:

class istream : public virtual ios {

  //...

  public:

     istream& operator>>(char*);    // строка

     istream& operator>>(char&);    // символ

     istream& operator>>(short&);

     istream& operator>>(int&);

     istream& operator>>(long&);

     istream& operator>>(float&);

     istream& operator>>(double&);

     //...

};



Функции ввода operator>> определяются так:

istream& istream::operator>>(T& tvar)

{

  // пропускаем обобщенные пробелы

  // каким-то образом читаем T в`tvar'

  return *this;

}

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

int readints(Vector<int>& v)

// возвращаем число прочитанных целых

{

  for (int i = 0; i<v.size(); i++)

  {

     if (cin>>v[i]) continue;

     return i;

  }

  // слишком много целых для размера Vector

  // нужна соответствующая обработка ошибки

}

Появление значения с типом, отличным от int, приводит к прекращению операции ввода, и цикл ввода завершается. Так, если мы вводим

1 2 3 4 5. 6 7 8.

то функция readints() прочитает пять целых чисел

1 2 3 4 5

Символ точка останется первым символом, подлежащим вводу. Под пробелом, как определено в стандарте С, понимается обобщенный пробел, т.е. пробел, табуляция, конец строки, перевод строки или возврат каретки. Проверка на обобщенный пробел возможна с помощью функции isspace() из файла <ctype.h>.

В качестве альтернативы можно использовать функции get():

class istream : public virtual ios {

  //...

  istream& get(char& c);                     // символ

  istream& get(char* p, int n, char ='n');   // строка

};

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

Функция istream::get(char&) вводит один символ в свой параметр. Поэтому программу посимвольного копирования можно написать так:


main()

{

  char c;

  while (cin.get(c)) cout << c;

}

Такая запись выглядит несимметрично, и у операции >> для вывода символов

есть двойник под именем put(), так что можно писать и так:

main()

{

  char c;

  while (cin.get(c)) cout.put(c);

}

Функция с тремя параметрами istream::get() вводит в символьный вектор не менее n символов, начиная с адреса p. При всяком обращении к get() все символы, помещенные в буфер (если они были), завершаются 0, поэтому если второй параметр равен n, то введено не более n-1 символов. Третий параметр определяет символ, завершающий ввод. Типичное использование функции get() с тремя параметрами сводится к чтению строки в буфер заданного размера для ее дальнейшего разбора, например так:

void f()

{

  char buf[100];

  cin >> buf;                      // подозрительно

  cin.get(buf,100,'\n');           // надежно

  //...

}

Операция cin>>buf подозрительна, поскольку строка из более чем 99 символов переполнит буфер. Если обнаружен завершающий символ, то он остается в потоке первым символом подлежащим вводу. Это позволяет проверять буфер на переполнение:

void f()

{

  char buf[100];

  cin.get(buf,100,'\n');   // надежно

  char c;

  if (cin.get(c) && c!='\n') {

     // входная строка больше, чем ожидалось

  }

  //...

}

Естественно, существует версия get() для типа unsigned char.

В стандартном заголовочном файле <ctype.h> определены несколько

функций, полезных для обработки при вводе:

int isalpha(char)                  // 'a'..'z' 'A'..'Z'

int isupper(char)                  // 'A'..'Z'

int islower(char)                  // 'a'..'z'

int isdigit(char)                  // '0'..'9'

int isxdigit(char)                 // '0'..'9' 'a'..'f' 'A'..'F'

int isspace(char)                  // ' ' '\t' возвращает конец строки

                                   // и перевод формата

int iscntrl(char)                  // управляющий символ в диапазоне

                                   // (ASCII 0..31 и 127)



int ispunct(char)                  // знак пунктуации, отличен от

                                   // приведенных выше

int isalnum(char)                  // isalpha() | isdigit()

int isprint(char)                  // видимый: ascii ' '..'~'

int isgraph(char)                  // isalpha() | isdigit() | ispunct()

int isascii(char c)   { return 0<=c && c<=127; }

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

(('a'<=c && c<='z') || ('A'<=c && c<='Z')) // буква

которое не только утомительно писать, но оно может быть и ошибочным (на машине с кодировкой EBCDIC оно задает не только буквы), лучше использовать вызов стандартной функции isalpha(), который к тому же более эффективен. В качестве примера приведем функцию eatwhite(), которая читает из потока обобщенные пробелы:

istream& eatwhite(istream& is)

{

  char c;

  while (is.get(c)) {

     if (isspace(c)==0) {

       is.putback(c);

       break;

     }

  }

  return is;

}

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


Содержание раздела