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

   


Назад  |  Вперёд

Класс 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(), которая возвращает символ в поток, и он становится первым подлежащим чтению.


Назад  |  Вперёд


OtDiatlovaOU
К началу книги

Оформление и дизайн книги OtDiatlovaOU.
Вся книга, архив.