Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 154
Текст из файла (страница 154)
Третий аргумент, (егт, задает терминальный символ. Типичное применение трех- аргументной функции яе(() — считывание физической строки текста (ограничен- Операция» предназначена для форматированного ввода, то есть для чтения объектов ожидаемого типа и формата. Когда мы хотим читать символы без предположений об их смысле, мы используем функции неформатированного ввода (ип(огта((е()! при(Хипс((опз): 132 Глава 2). Потоки ной, например, терминальным символом '~п') в буфер фиксированного размера для ее последующего анализа: Если терминальный символ обнаружен, он остается первым непрочитанным символом в потоке. Никогда не вызывайте функцию лег() дважды подряд без удаления терминального символа, как в следующем примере: гоЫ зиЫе еггог() ( сваг ЬиГ[150] ) н4и!е(ст) ( с!п.яег (ьиГ, 150); У читаем строку сонг «ЬиГ) У печатаем строку У Оорз: забыли удалить ')п' из ст - следующий ее(() не сработает ) Отсюда следует, что лучше использовать яег11пе() вместо яет().
функция яег11пе() во всем аналогичнаяег(), но она удаляеттерминальный символ из потока 1зггеапз. Например: гоЫГ( ) ( слог пиит[МАХ АМОКО] [МАХ юг)Е]; Х МАХ НгОИ) массив из МАХ 1.ь'чЕ ~У символов в каждом ]пгз'= 0; вЫ1е(сгп.лез11пе(пюгд[гн-),МАХ й!МЕ, ",и' ) ьа ]<МАХ ИОЕО); Х...
) Когда эффективность не является важнейшей целью, лучше считывать символы в строку типа згг1пи ($3.6, $20.3.15), ибо в этом случае отпадают проблемы с распределением памяти и переполнением буфера. Функции беГО, яе111пе() и геаг1() нужны для высочайшей производительности, за которую приходится платить запутанным интерфейсом. При этом также отпадает необходимость в повторном чтении для того, чтобы выяснить причины окончания ввода, а также имеется возможность надежно ограничить ввод символов заданным их числом. Вызов ген(р,п) читает не более и символов ар[0] ..р[п-1], Эта функция не полагается на терминальный символ и не добавляет нуль после помещенных в массив символов. Как следствие, она действительно считывает п символов (а не п-1).
Другими словами, она просто читает символы и не пытается превратить считанное в С-строку. гоЫГ( ) ( сваг ЬиГ[100]; с]п» ЬиГ; сгп.лег(биГ,100, ' Хп ' ); ~У... ) У подозрение: когда-нибудь приведет к переполнению Х безопасно 21.3. Ввод 733 Функция !яви«е() читает символы так же, как функция «еаИ(), но нигде не хранит их. Как и «вам(), она читает ровно п символов. Вызов функции !япо«е () без аргументов приводит к чтению (отбрасыванию) одного (следующего) символа.
Как и функция яеЖпе(), функция «еаь!() имеет необязательный параметр, означающий терминальный символ, который она и отбрасывает, если добирается до него. Отметим, что по умолчанию терминальным символом для функции !япо«е() служит вконец файла». Для всех перечисленных функций неформатированного ввода совсем не очевидно, что именно прекращает процесс ввода, и каков конкретный критерий завершения их работы. Однако всегда имеется возможность выяснить, не достигли ли мы конца файла (521.3.3). Кроме того, имеется функция ясоипг(), возвращающая число символов, считанных из потока последним вызовом функции неформатированного ввода.
Например: ио1а'«еай а 11пе(1п< игах) ( // ... (!'(с1п.!а!!О ) ( с!п.с!ее«(); с(п.(апоге (тах, ' ь ' ) ь //Оорм неправильный формат ввода //очистить флаги ввода Я2!.3.3! //пропуск до точки с запетой (!'(! с1п ) ( //оорск достигли конца потока е!ве (г(с(п. асоип! () ==тах) ( У оорек прочли максимальное количество символов ) е1ле ( // нанти и удалили точку с лаплтой ) ) ) «оЫЯипв/дней сйа«ь р) ( Йьг 1ь н4и1е ( (! = с1п.яег ( ) да 1! =ЕОГ) К сожалению, если прочитано максимальное количество символов, то уже невозможно установить, был ли достигнут терминальный символ (в качестве последнего символа).
Функция яе«О без аргумента эквивалентна функции «его!га«О из <сеть!!о> (821.8). Она просто читает символ и возвращает его численное значение. Таким образом, она избегает делать предположения о том, какой использовался символьный тип. Если нет никакого символа, то яег() возвращает подходящий маркер «конца файла» (то есть 1«а!1в 1уре:: еоуО этого потока) и устанавливает поток в состояние еоу" (521.3.3).
Например: Глава 21. Потоки 734 ( ?? ... ) ) Здесь ЕОŠ— зто возврат функции еоу() из обычных с7заг ггагь для типа айаг. ЕОЕ представлен в заголовочном файле <гол(гент>. Таким образом, вместо цикла можно было бы сделать вызов генея (р, МАХ И(7'), но мы написали явный цикл для того, чтобы, например, просматривать каждый символ по мере поступления такового. Уже говорилось, что сила языка С заключается в умении считать символ и принять решение (причем быстро), что с ним ничего не надо делать. Это важное достоинство, которое нельзя недооценивать, и в языке С++ оно сохранено. В стандартном заголовочном файле <сощуре> определено несколько функций, полезных при обработке ввода (З20.4.2).
Например, функция еапгййе (), считывающая пробельные символы, могла быть определена следующим образом: Ьзгеать еаигвае (Ьггеатв Ь) ( сааг с; илве(гз.лег(с) ) (Т(!Ьзрасе(с) ) ?? с - это пробельный символ? ( Ь.ригвава (с); ?? помещаем с назад в буфер ввода Ь еааз ) ) гегигп Ь; Вызов гз.рнгЬасй(с) превращает с в следующий символ, читаемый из потока Ь Я21.б.4). 21.3.5. Ввод пользовательских типов Как и в случае вывода, для пользовательских типов данных можно определять операции ввода. Однако для операций ввода важно, чтобы их второй аргумент не был константной ссылкой. Например: Ьиеать орега(ог» (Ьггеать з, сотр(ехь а) ?* форматы ввода для совр(ех Гзч означает число с плавающей запятой): Гали (У) или Щ ь ( аоибге ге = О, гт = О; слог с = Ог з»с; (Г(с ==' (' ) 21.3. Ввод з»ге»с( (!'(с == ', ' ] з» 1т» с( (1'(с ! = ' ) ') з.с(еаг(юз Ьазе::!а11Ы() ( ) е(зе ( з.ри(Ьасй (с) ( з»ге( ) Д(з) а = сотр(ех(ге, ип) ( ге(игп з; ) Несмотря на краткость кода обработки ошибок, он в состоянии выловить большинство их видов.
Локальная переменная с инициализируется во избежание случайных совпадений ее значения с ' ( ' после первой неудавшейся операции». Заключительная проверка состояния потока гарантирует, что переменная а изменяется лишь в том случае, если все прошло успешно. Если обнаруживается ошибка форматирования, состояние потока устанавливается в!а!!Ы(. Состояние потока не устанавливается в Ьав(ЬЬ(, потому что поток сам по себе не разрушен. Пользователь может восстановить состояние потока (используя с1еаг() ) и, возможно, обойти проблему и извлечь полезные данные из потока.
Операция по восстановлению состояния потока названа с1еа( (), поскольку ее типичное назначение — сброс состояние потока в яоо(!О; юз Ьазе::яот!Ы(— умолчательное значение аргумента для функции с1еаг () 52).3.3). 21.3.6. Исключения Проверять наличие ошибок после каждой операции ввода/вывода неудобно, и поэтому основная причина возникновения таких ошибок заключается в том, что отсутствуют необходимые проверки в важных местах программы. В частности, типичным является отсутствие проверки наличия ошибок при выводе, хотя они встречаются и в этом случае.
Единственной функцией, непосредственно изменяюшей состояние потока, является с1еаг () . Поэтому единственный способ заметить изменение состояния потока — это попросить функцию с1еаг() генерировать исключения. Функция-член схсерйопз() класса Ь(ю(с (оз как раз и делает это: (етр1а(с<с(ат СЬ, с(азз Тг = сваг (га(т<СЬ» с1азз Ьаз(с !оз: риЫ(с юз Ьазе ( риЫ(с: // .. с(ат (а((иге( о' класс исключений !ель з!4. 10) юта(е ехсер(1опз () сопл(( г( получить состояние исключений гоЫ ехсер((опз (юта(е ехсерн ( (!установить состояние исключения 736 Глава 21 Потоки Например: сои<.
ехсер<<олз (юз Ьазе:: Ьа<<Ь<1 ) <оз Ьазе::/аИЬ1< ) юз вазе:: ео/Ь(<) требует, чтобы с1еа< ( ) генерировала исключение <оз Ьаяе::7аИпге, если поток сои< переходит в состояние ЬаИ, УаИ или ео/ — другими словами, если любая операция вывода не выполнилась безупречно. Если необходимо, можно проверить сои< с тем, чтобы выяснить в точности, что произошло.