|
Программирование >> Разработка устойчивых систем
linclude <iostream> using namespace std: int mainO { int i: cin i: float f: cin f: char c: cin c: char buf[100]: cin buf: cout i = i endl cout f = f endl cout c = с endl cout buf = buf endl: cout flush: cout hex Ox i endl: } III:- Попробуем передать этой программе такой набор входных данных: 12 1.4 с this is а test Казалось бы, данные должны распределиться следующим образом: this is а test Однако тест дает несколько неожиданный результат: i = 12 f = 1.4 с = с buf = this Oxc Переменной buf достается только первое слово, потому что функция ввода считает пробел после this признаком конца ввода. Кроме того, если непрерывная входная строка превысит объем блока памяти, выделенного для buf, произойдет переполнение буфера. На практике обычно бывает проще получить от интерактивной программы строку в виде последовательности символов, сканировать ее и выполнить необходимые преобразования, после того как строка окажется в буфере. В этом случае вам не придется беспокоиться о том, что функция ввода подавится непредвиденными данными. Другой важный фактор - сама концепция интерфейса командной строки. Она была вполне оправданна в прошлом, когда консоль была обычной комбинацией пишущей машинки и телевизора, но мир быстро меняется, и сейчас в нем преобладают графические пользовательские интерфейсы (Graphical User Interface, GUI). Найдется ли место консольному вводу-выводу в таком мире? Гораздо разумнее ограничиться применением объекта cin для простейших примеров или тестов, и выбрать один из перечисленных ниже вариантов. Прочитать входные данные из файла - вскоре вы убедитесь, что потоки ввода-вывода чрезвычайно упрощают работу с файлами. Файловые потоки нормально работают в графических средах. Прочитать входные данные, не пытаясь преобразовывать их, как предлагалось ранее. Когда входные данные окажутся в надежном месте, где преобразование не сможет ничего испортить, их можно спокойно отсканировать. С выводом дело обстоит иначе. В графических средах объект cout работает не всегда, поэтому вывод приходится посылать в файл (средствами, идентичными выводу в cout) или использовать механизмы вывода графической среды. Впрочем, если объект cout нормально работает, для вывода данных смысл имеет задействовать его. В любом случае потоковые функции форматирования вывода приносят несомненную пользу. Стоит упомянуть и другой распространенный прием, сокращающий время компиляции больших проектов. Подумайте, как бы вы объявили упоминавшиеся ранее потоковые операторы Date в заголовочном файле? Необходимо объявить только прототипы функций, поэтому включать весь заголовок <iostream> в файл Date.h не обязательно. В обычной ситуации вы бы использовали только объявление класса: class ostream: Это классический принцип отделения интерфейса от реализации, часто называемый опережающим объявлением (а ostream в этой точке программы рассматривается как неполный тип, потому что компилятор еще не видел определения класса). Однако такое объявление не будет работать по двум причинам: потоковые классы определяются в пространстве имен std; потоковые классы представляют собой шаблоны. Правильное объявление выглядит так: namespace std { tempiate<class charT. class traits = char traits<charT> > class basic ostream: typedef bas1c ostream<char> ostream: Как видите, потоковые классы используют классы характеристик, упоминавшиеся в главе 3. Набирать этот текст для каждого потокового класса, на который вы хотите сослаться, было бы крайне утомительно, поэтому в стандарте определяется заголовочный файл <iosfwcl>, который делает это за вас. Тогда заголовок Date принимает следующий вид: Date.h #1nclude <1osfwd> class Date { friend std::ostream& operator (std::ostream&. const Date&): friend std::1stream& operator (std::1stream&. Date&): И т. д. Построчный ввод Для получения одной строки входных данных у вас имеются три инструмента: функция класса get(); функция класса getline(); глобальная функция getline(), определенная в заголовочном файле <string>. Первые две функции получают три аргумента: указатель на символьный буфер для хранения результата; размер буфера (для контроля переполнения); завершающий символ, по которому прекращается чтение данных. По умолчанию завершающим символом является символ \п (конец строки); обычно именно он чаще всего используется на практике. Встречая завершающий символ во входных данных, обе функции записывают ноль в выходной буфер. Тогда чем же они отличаются? Отличие тонкое, но важное: встречая завершитель, функция get() останавливается, но не извлекает его из входного потока. Таким образом, если немедленно вызвать get() с тем же завершителем, функция вернется, не прочитав ни одного символа (поэтому придется либо вызвать другую функцию, либо get() с другим завершителем). С другой стороны, функция getline() извлекает завершитель из входного потока, хотя и не сохраняет его в буфере. Функция getline(), определенная в заголовочном файле <string>, весьма удобна. Она не принадлежит какому-либо классу, а является автономной функцией, объявленной в пространстве имен std. Ей передаются только два обязательных аргумента: входной поток и заполняемый объект string. Как и одноименная функция класса, она читает символы до обнаружения первого вхождения завершителя (по умолчанию \п) и поглощает завершитель. Преимущество этой функции состоит в том, что данные читаются в объект string, поэтому вам не придется беспокоиться о размере буфера. Как правило, при построчном чтении текстовых файлов используется одна из разновидностей функции getline(). Перегруженные версии get() Функция get() также существует в трех перегруженных версиях. Первая версия вызывается без аргументов и возвращает следующий символ в виде int; вторая версия сохраняет символ в аргументе char по ссылке; третья версия сохраняет символ в буфере другого потока ввода-вывода. Последняя возможность будет рассмотрена в этой главе. Неформатированный ввод Если вы точно знаете, с какими данными работаете, и хотите напрямую записать байты в переменную, массив или структуру памяти, воспользуйтесь функцией неформатированного ввода-вывода геас1(). Первый аргумент этой функции содержит указатель на приемный адрес, а второй - количество читаемых байтов. Функция особенно удобна при загрузке данных, предварительно сохраненных в файле (например, парной функцией write() выходного потока - конечно, для того же компилятора). Примеры использования всех этих функций будут приведены далее.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |