Б. Страуструп - Язык программирования С++ (1119446), страница 74
Текст из файла (страница 74)
Контроль переполнения не нужен, поскольку ost знает свойразмер и при заполнении перейдет в состояние, определяемое fail(). Затем функция display можетпослать сообщение в "настоящий" выходной поток. Такой прием наиболее подходит в тех случаях,когда окончательная операция вывода предназначена для записи на более сложное устройство, чемтрадиционное, ориентированное на последовательность строк, выводное устройство.
Например, текстиз ost может быть помещен в фиксированную область на экране.Аналогично, istrstream является вводным строковым потоком, читающим из последовательностисимволов, заканчивающейся нулем:void word_per_line(char v[], int sz)/*печатать "v" размером "sz" по одному слову в строке*/{istrstream ist(v,sz); // создать istream для vchar b2[MAX];// длиннее самого длинного словаwhile (ist>>b2) cout <<b2 << "\n";}Завершающий нуль считается концом файла.Строковые потоки описаны в файле <strstream.h>.10.5.3 БуферизацияВсе операции ввода-вывода были определены без всякой связи с типом файла, но нельзя одинаковоработать со всеми устройствами без учета алгоритма буферизации. Очевидно, что потоку ostream,привязанному к строке символов, нужен не такой буфер, как ostream, привязанному к файлу.
Такиевопросы решаются созданием во время инициализации разных буферов для потоков разных типов. Носуществует только один набор операций над этими типами буферов, поэтому в ostream нет функций,код которых учитывает различие буферов. Однако, функции, следящие за переполнением иобращением к пустому буферу, являются виртуальными. Это хороший пример применения виртуальныхфункций для единообразной работы с эквивалентными логически, но различно реализованнымиструктурами, и они вполне справляются с требуемыми алгоритмами буферизации. Описание буферапотока в файле <iostream.h> может выглядеть следующим образом:class streambuf {// управление буфером потокаprotected:char* base;// начало буфераchar* pptr;// следующий свободный байтchar* gptr;// следующий заполненный байтchar* eptr;// один из указателей на конец буфераchar alloc;// буфер, размещенный с помощью "new"//...// Опустошить буфер:// Вернуть EOF при ошибке, 0 - удачаvirtual int overflow(int c = EOF);// Заполнить буфер:// Вернуть EOF в случае ошибки или конца входного потока,// иначе вернуть очередной символvirtual int underflow();//...278Бьерн Страуструп.Язык программирования С++public:streambuf();streambuf(char* p, int l);virtual ~streambuf();int snextc()// получить очередной символ{return (++gptr==pptr) ? underflow() : *gptr&0377;}int allocate();// отвести память под буфер//...};Подробности реализации класса streambuf приведены здесь только для полноты представления.
Непредполагается, что есть общедоступные реализации, использующие именно эти имена. Обратитевнимание на определенные здесь указатели, управляющие буфером; с их помощью простыепосимвольные операции с потоком можно определить максимально эффективно (и причем однократно)как функции-подстановки. Только функции overflow() и underflow() требует своей реализации длякаждого алгоритма буферизации, например:class filebuf : public streambuf {protected:int fd;// дескриптор файлаchar opened;// признак открытия файлаpublic:filebuf() { opened = 0; }filebuf(int nfd, char* p, int l): streambuf(p,l) { /* ... */ }~filebuf() { close(); }int overflow(int c=EOF);int underflow();filebuf* open(char *name, ios::open_mode om);int close() { /* ...
*/ }//...};int filebuf::underflow()// заполнить буфер из "fd"{if (!opened || allocate()==EOF) return EOF;int count = read(fd, base, eptr-base);if (count < 1) return EOF;gptr = base;pptr = base + count;return *gptr & 0377; // &0377 предотвращает размножение знака}За дальнейшими подробностями обратитесь к руководству по реализации класса streambuf.10.6 Ввод-вывод в СПоскольку текст программ на С и на С++ часто путают, то путают иногда и потоковый ввод-вывод С++ ифункции ввода-вывода семейства printf для языка С. Далее, т.к.
С-функции можно вызывать изпрограммы на С++, то многие предпочитают использовать более знакомые функции ввода-вывода С.По этой причине здесь будет дана основа функций ввода-вывода С. Обычно операции ввода-вывода наС и на С++ могут идти по очереди на уровне строк. Перемешивание их на уровне посимвольного вводавывода возможно для некоторых реализаций, но такая программа может быть непереносимой.Некоторые реализации потоковой библиотеки С++ при допущении ввода-вывода на С требуют вызовастатической функции-члена ios::sync_with_stdio().В общем, потоковые функции вывода имеют перед стандартной функцией С printf() то преимущество,что потоковые функции обладают определенной типовой надежностью и единообразно определяют279Бьерн Страуструп.Язык программирования С++вывод объектов предопределенного и пользовательского типов.Основная функция вывода С естьint printf(const char* format, ...)и она выводит произвольную последовательность параметров в формате, задаваемом строкойформатирования format.
Строка форматирования состоит из объектов двух типов: простые символы,которые просто копируются в выходной поток, и спецификации преобразований, каждая из которыхпреобразует и печатает очередной параметр. Каждая спецификация преобразования начинается ссимвола %, напримерprintf("there were %d members present.",no_of_members);Здесь %d указывает, что no_of_members следует считать целым и печатать как соответствующуюпоследовательность десятичных цифр. Если no_of_members==127, то будет напечатаноthere were 127 members present.Набор спецификаций преобразований достаточно большой и обеспечивает большую гибкость печати.За символом % может следовать:-необязательный знак минус,преобразованного значения;задающийвыравниваниевлевовуказанномполедляdнеобязательная строка цифр, задающая ширину поля; если в преобразованном значении меньшесимволов, чем ширина строки, то оно дополнится до ширины поля пробелами слева (или справа,если дана спецификация выравнивания влево); если строка ширины поля начинается с нуля, тодополнение будет проводится нулями, а не пробелами;.необязательный символ точка служит для отделения ширины поля от последующей строки цифр;dнеобязательная строка цифр, задающая точность, которая определяет число цифр последесятичной точки для значений в спецификациях e или f, или же задает максимальное числопечатаемых символов строки;*для задания ширины поля или точности может использоваться * вместо строки цифр.
В этомслучае должен быть параметр целого типа, который содержит значение ширины поля илиточности;hнеобязательный символ h указывает, что последующая спецификация d, o, x или u относится кпараметру типа короткое целое;lнеобязательный символ l указывает, что последующая спецификация d, o, x или u относится кпараметру типа длинное целое;%обозначает, что нужно напечатать сам символ %; параметр не нужен;cсимвол, указывающий тип требуемого преобразования. Символы преобразования и их смыслследующие:d Целый параметр выдается в десятичной записи;o Целый параметр выдается в восьмеричной записи;x Целый параметр выдается в шестнадцатеричной записи;fВещественный или с двойной точностью параметр выдается в десятичной записи вида []ddd.ddd, где число цифр после точки равно спецификации точности для параметра.
Еслиточность не задана, печатается шесть цифр; если явно задана точность 0, точка и цифры посленее не печатаются;e Вещественный или с двойной точностью параметр выдается в десятичной записи вида []d.ddde+dd; здесь одна цифра перед точкой, а число цифр после точки равно спецификацииточности для параметра; если она не задана печатается шесть цифр;g Вещественный или с двойной точностью параметр печатается по той спецификации d, f или e,которая дает большую точность при меньшей ширине поля;280Бьерн Страуструп.Язык программирования С++c Символьный параметр печатается.
Нулевые символы игнорируются;s Параметр считается строкой (символьный указатель), и печатаются символы из строки донулевого символа или до достижения числа символов, равного спецификации точности; но,если точность равна 0 или не указана, печатаются все символы до нулевого;p Параметр считается указателем и его вид на печати зависит от реализации;u Беззнаковый целый параметр печатается в десятичной записи.Несуществующее поле или поле с шириной, меньшей реальной, приведет к усечению поля. Дополнениепробелами происходит, если только спецификация ширины поля больше реальной ширины.
Нижеприведен более сложный пример:char* src_file_name;int line;char* line_format = "\n#line %d \"%s\"\n";main(){line = 13;src_file_name = "C++/main.c";printf("int a;\n");printf(line_format,line,src_file_name);printf("int b;\n");}в котором печатаетсяint a;#line 13 "C++/main.c"int b;Использование printf() ненадежно в том смысле, что нет никакого контроля типов.