Бьерн Страуструп. Язык программирования С++



бет84/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   80   81   82   83   84   85   86   87   ...   124

10.3 ВВОД


Ввод во многом сходен с выводом. Есть класс istream, который реализует

операцию ввода >> ("ввести из" - "input from") для небольшого набора

стандартных типов. Для пользовательских типов можно определить функцию

operator>>.

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& v)

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

{

for (int i = 0; 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()

из файла .

В качестве альтернативы можно использовать функции 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.

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

функций, полезных для обработки при вводе:
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(), которая возвращает символ в

поток, и он становится первым подлежащим чтению.



10.3.2 Состояния потока


С каждым потоком (istream или ostream) связано определенное состояние.

Нестандартные ситуации и ошибки обрабатываются с помощью проверки и

установки состояния подходящим образом.

Узнать состояние потока можно с помощью операций над классом ios:
class ios { //ios является базовым для ostream и istream

//...


public:

int eof() const; // дошли до конца файла

int fail() const; // следующая операция будет неудачна

int bad() const; // поток испорчен

int good() const; // следующая операция будет успешной

//...


};
Последняя операция ввода считается успешной, если состояние задается

good() или eof(). Если состояние задается good(), то последующая

операция ввода может быть успешной, в противном случае она будет

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

не good(), считается пустой операцией. Если произошла неудача при

попытке чтения в переменную v, то значение v не изменилось (оно не

изменится, если v имеет тип, управляемый функциями члена из istream

или ostream). Различие между состояниями, задаваемыми как fail() или

как bad() уловить трудно, и оно имеет смысл только для разработчиков

операций ввода. Если состояние есть fail(), то считается, что поток

не поврежден, и никакие символы не пропали; о состоянии bad() ничего

сказать нельзя.

Значения, обозначающие эти состояния, определены в классе ios:
class ios {

//...


public:

enum io_state {

goodbit=0,

eofbit=1,

filebit=2,

badbit=4,

};

//...


};
Истинные значения состояний зависят от реализации, и указанные значения

приведены только, чтобы избежать синтаксически неправильных конструкций.

Проверять состояние потока можно следующим образом:
switch (cin.rdstate()) {

case ios::goodbit:

// последняя операция с cin была успешной

break;


case ios::eofbit:

// в конце файла

break;

case ios::filebit:



// некоторый анализ ошибки

// возможно неплохой

break;

case ios::badbit:



// cin возможно испорчен

break;


}
В более ранних реализациях для значений состояний использовались

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

пространства именования, поэтому новые имена доступны только в пределах

класса ios. Если вам необходимо использовать старые имена в сочетании с

новой библиотекой, можно воспользоваться следующими определениями:
const int _good = ios::goodbit;

const int _bad = ios::badbit;

const int _file = ios::filebit;

const int _eof = ios::eofbit;


typedef ios::io_state state_value ;
Разработчики библиотек должны заботится о том, чтобы не добавлять

новых имен к глобальному пространству именования. Если элементы

перечисления входят в общий интерфейс библиотеки, они всегда

должны использоваться в классе с префиксами, например, как ios::goodbit

и ios::io_state.

Для переменной любого типа, для которого определены операции



<< и >>, цикл копирования записывается следующим образом:
while (cin>>z) cout << z << '\n';
Если поток появляется в условии, то проверяется состояние потока, и

условие выполняется (т.е. результат его не 0) только для состояния

good(). Как раз в приведенном выше цикле проверяется состояние потока

istream, что является результатом операции cin>>z. Чтобы узнать,

почему произошла неудача в цикле или условии, надо проверить состояние.

Такая проверка для потока реализуется с помощью операции

приведения (7.3.2).

Так, если z является символьным вектором, то в приведенном цикле

читается стандартный ввод и выдается для каждой строки стандартного

вывода по одному слову (т.е. последовательности символов, не являющихся

обобщенными пробелами). Если z имеет тип complex, то в этом цикле

с помощью операций, определенных в 10.2.2 и 10.2.3, будут копироваться

комплексные числа. Шаблонную функцию копирования для потоков со

значениями произвольного типа можно написать следующим образом:


complex z;

iocopy(z,cin,cout); // копирование complex


double d;

iocopy(d,cin,cout); // копирование double

char c;

iocopy(c,cin,cout); // копирование char


Поскольку надоедает проверять на корректность каждую операцию ввода-

вывода, то распространенным источником ошибок являются именно те места в

программе, где такой контроль существенен. Обычно операции вывода не

проверяют, но иногда они могут завершиться неудачно. Потоковый ввод-

вывод разрабатывался из того принципа, чтобы сделать исключительные

ситуации легкодоступными, и тем самым упростить обработку ошибок

в процессе ввода-вывода.



Достарыңызбен бөлісу:
1   ...   80   81   82   83   84   85   86   87   ...   124




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет