Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование на C / C++ / Ален И. Голуб. Правила программирования на Си и Си++ [pdf]

.pdf
Скачиваний:
234
Добавлен:
02.05.2014
Размер:
5.67 Mб
Скачать

С++ для начинающих

1022

#include <fstream> #include <string> #include <vector> #include <algorithm> int main()

{

string ifile;

cout << "Введите имя файла для сортировки: "; cin >> ifile;

// сконструировать объект класса ifstream для ввода из файла ifstream infile( ifile.c_str() );

if ( ! infile ) {

cerr << "ошибка: не могу открыть входной файл: " << ifile << endl;

return -1;

}

string ofile = ifile + ".sort";

// сконструировать объект класса ofstream для вывода в файл ofstream outfile( ofile.c_str() );

if ( ! outfile) {

cerr << "ошибка: не могу открыть выходной файл: " << ofile << endl;

return -2;

}

string buffer;

vector< string, allocator > text;

int cnt = 1;

while ( infile >> buffer ) { text.push_back( buffer );

cout << buffer << (cnt++ % 8 ? " " : "\n" );

}

sort( text.begin(), text.end() );

// выводим отсортированное множество слов в файл vector< string >::iterator iter = text.begin(); for ( cnt = 1; iter != text.end(); ++iter, ++cnt )

outfile << *iter

<< (cnt % 8 ? " " : "\n" );

return 0;

}

Вот пример сеанса работы с этой программой. Нас просят ввести файл для сортировки. Мы набираем alice_emma (набранные на клавиатуре символы напечатаны полужирным шрифтом). Затем программа направляет на стандартный вывод все, что прочитала из файла:

Введите имя файла для сортировки: alice_emma Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

tells her, magical but untamed. "Daddy, shush, there

С++ для начинающих

1023

is no such creature," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?"

Далее программа выводит в файл outfile отсортированную последовательность строк. Конечно, на порядок слов влияют знаки препинания; в следующем разделе мы это исправим:

"Daddy, "I A Alice Daddy Daddy,

Emma Her

Shyly, a alive,

almost asks, at

beautiful bird

bird, blows but

creature,"

fiery fiery flight. flowing

hair, hair. has

he her her

her,

him

him, in is is it like long

looks

says she

magical mean, more. no red

same

she shush, such

tell tells

tells the the

there there?" through time

to untamed. wanting when

wind

 

 

 

(В разделе 20.6 мы познакомимся с файловым вводом/выводом более подробно.)

Библиотека iostream поддерживает также ввод/вывод в область памяти, при этом поток связывается со строкой в памяти программы. С помощью потоковых операторов ввода/вывода мы можем записывать данные в эту строку и читать их оттуда. Объект для строкового ввода/вывода определяется как экземпляр одного из следующих классов:

istringstream, производный от istream, читает из строки;

ostringstream, производный от ostream, пишет в строку;

stringstream, производный от iostream, выполняет как чтение, так и запись.

Для использования любого из этих классов в программу нужно включить заголовочный файл

#include <sstream>

(Файл sstream уже включает iostream, так что включать оба файла необязательно.) В

следующем фрагменте объект класса ostringstream используется для форматирования сообщения об ошибке, которое возвращается вызывающей программе.

С++ для начинающих

1024

#include <sstream>

string program_name( "our_program" ); string version( 0.01 );

// ...

string mumble( int *array, int size )

{

if ( ! array ) { ostringstream out_message;

out_message << "ошибка: "

<<program_name << "--" << version

<<": " << __FILE__ << ": " << __LINE__

<<" -- указатель равен 0; "

<<" а должен адресовать массив.\n";

//возвращаем строку, в которой находится сообщение return out_message.str();

}

// ...

}

(В разделе 20.8 мы познакомимся со строковым вводом/выводом более подробно.)

Потоки ввода/вывода поддерживают два предопределенных типа: char и wchar_t. В этой главе мы расскажем только о чтении и записи в потоки данных типа char. Помимо них, в библиотеке iostream имеется набор классов и объектов для работы с типом wchar_t. Они отличаются от соответствующих классов, использующих тип char, наличием префикса ‘w’. Так, объект стандартного ввода называется wcin, стандартного вывода wcout, стандартного вывода для ошибок wcerr. Но набор заголовочных файлов для char и wchar_t один и тот же.

Классы для ввода/вывода данных типа wchar_t называются wostream, wistream, wiostream, для файлового ввода/вывода wofstream, wifstream, wfstream, а для строкового wostringstream, wistringstream, wstringstream.

20.1. Оператор вывода <<

Оператор вывода обычно применяется для записи на стандартный вывод cout.

#include <iostream>

int main()

{

cout << "сплетница Анна Ливия\n";

Например, программа

}

печатает на терминале строку:

сплетница Анна Ливия

С++ для начинающих

1025

Имеются операторы, принимающие аргументы любого встроенного типа данных,

включая const char*,

а также типов string и complex из стандартной библиотеки.

Любое выражение, включая вызов функции, может быть аргументом оператора вывода при условии, что результатом его вычисления будет тип, принимаемый каким-либо

#include <iostream> #include <string.h>

int main()

{

cout << "Длина 'Улисс' равна:\t"; cout << strlen( "Улисс" );

cout << '\n';

cout << "Размер 'Улисс' равен:\t"; cout << sizeof( "Улисс" );

cout << endl;

вариантом этого оператора. Например, программа

}

выводит на терминал следующее:

Длина 'Улисс' равна:7 Размер 'Улисс' равен:8

endl это манипулятор вывода, который вставляет в выходной поток символ перехода на новую строку, а затем сбрасывает буфер объекта ostream. (С буферизацией мы познакомимся в разделе 20.9.)

Операторы вывода, как правило, удобнее сцеплять в одну инструкцию. Например,

#include <iostream> #include <string.h>

int main()

{

// операторы вывода можно сцеплять

cout << "Длина 'Улисс' равна:\t"; << strlen( "Улисс" ) << '\n';

cout << "Размер 'Улисс' равен:\t" << sizeof( "Улисс" ) << endl;

предыдущую программу можно записать таким образом:

}

Сцепление операторов вывода (и ввода тоже) возможно потому, что результатом

выражения

cout << "некоторая строка";

С++ для начинающих

1026

служит левый операнд оператора вывода, т.е. сам объект cout. Затем этот же объект передается следующему оператору и далее по цепочке (мы говорим, что оператор << левоассоциативен).

Имеется также предопределенный оператор вывода для указательных типов, который печатает адрес объекта. По умолчанию адреса отображаются в шестнадцатеричном виде.

#include <iostream>

int main()

{

int i = 1024; int *pi = &i;

cout <<

"i: " << i

 

<<

"\t&i:\t" << &i << '\n';

cout <<

"*pi: "

<< *pi

<<"\tpi:\t" << pi << endl

<<"\t\t&pi:\t" << &pi << endl;

Например, программа

}

выводит на терминал следующее:

i: 1024 &i: 0x7fff0b4 *pi: 1024 pi: 0x7fff0b4 &pi: 0x7fff0b0

Позже мы покажем, как напечатать адреса в десятичном виде.

Следующая программа ведет себя странно. Мы хотим напечатать адрес, хранящийся в

#include <iostream>

const char *str = "vermeer"; int main()

{

const char *pstr = str; cout << "Адрес pstr равен: "

<< pstr << endl;

переменной pstr:

}

Но после компиляции и запуска программа неожиданно выдает такую строку:

Адрес pstr равен: vermeer

Проблема в том, что тип const char* интерпретируется как C-строка. Чтобы все же напечатать адрес, хранящийся в pstr, необходимо подавить обработку типа const

С++ для начинающих

1027

char* по умолчанию. Для этого мы сначала убираем спецификатор const, а затем приводим pstr к типу void*:

<< static_cast<void*>(const_cast<char*>(pstr))

Теперь программа выводит ожидаемый результат:

Адрес pstr равен: 0x116e8

#include <iostream>

inline void

max_out( int val1, int val2 )

{

cout << ( val1 > val2 ) ? val1 : val2;

}

int main()

{

int ix = 10, jx = 20;

cout << "Большее из " << ix

<< ", " << jx << " равно ";

max_out( ix, jx );

cout << endl;

Авот еще одна загадка. Нужно напечатать большее из двух чисел:

}

Однако программа выдает неправильный результат:

Большее из 10, 20 равно 0

Проблема в том, что оператор вывода имеет более высокий приоритет, чем оператор условного выражения, поэтому печатается результат сравнения val1 и val2. Иными словами, выражение

cout << ( val1 > val2 ) ? val1 : val2;

вычисляется как

(cout << ( val1 > val2 )) ? val1 : val2;

Поскольку val1 не больше val2, то результатом сравнения будет false, обозначаемый нулем. Чтобы изменить приоритет операций, весь оператор условного выражения следует заключить в скобки:

cout << ( val1 > val2 ? val1 : val2 );

Теперь результат получается правильный:

С++ для начинающих

1028

Большее из 10, 20 равно 20

Такого рода ошибку было бы проще найти, если бы значения литералов true и false типа bool печатались как строки, а не как 1 и 0. Тогда мы увидели бы строку:

Большее из 10, 20 равно false

и все стало бы ясно. По умолчанию литерал false печатается как 0, а true как 1. Это можно изменить, воспользовавшись манипулятором boolalpha(), что и сделано в

int main()

{

cout << "печать значений типа bool по умолчанию: "

<<true << " " << false

<<"\nи в виде строк: "

<<boolalpha()

<<true << " " << false

<<endl;

следующей программе:

}

Вот результат:

печать значений типа bool по умолчанию: 1 0

и в виде строк: true false

Для вывода массива, а также вектора или отображения, необходимо обойти все элементы

#include <iostream> #include <vector> #include <string>

string pooh_pals[] = {

"Тигра", "Пятачок", "Иа-Иа", "Кролик"

};

int main()

{

vector<string> ppals( pooh_pals, pooh_pals+4 );

vector<string>::iterator iter = ppals.begin(); vector<string>::iterator iter_end = ppals.end();

cout << "Это друзья Пуха: ";

for ( ; iter != iter_end; iter++ ) cout << *iter << " ";

cout << endl;

и напечатать каждый из них:

С++ для начинающих

1029

}

Вместо того чтобы явно обходить все элементы контейнера, выводя каждый по очереди, можно воспользоваться потоковым итератором ostream_iterator. Так выглядит эквивалентная программа, где используется эта техника (подробное обсуждение

#include <iostream> #include <algorithm> #include <vector> #include <string>

string pooh_pals[] = {

"Тигра", "Пятачок", "Иа-Иа", "Кролик"

};

int main()

{

vector<string> ppals( pooh_pals, pooh_pals+4 );

vector<string>::iterator iter = ppals.begin(); vector<string>::iterator iter_end = ppals.end();

cout << "Это друзья Пуха: ";

// копируем каждый элемент в cout ...

ostream_iterator< string > output( cout, " " ); copy( iter, iter_end, output );

cout << endl;

итератора ostream_iterator см. в разделе 12.4):

}

Программа печатает такую строку:

Это друзья Пуха: Тигра Пятачок Иа-Иа Кролик

Упражнение 20.1

string sa[4] = { "пух", "тигра", "пятачок", "иа-иа" }; vector< string > svec( sa, sa+4 );

string robin( "кристофер робин" ); const char *pc = robin.c_str(); int ival = 1024;

char blank = ' '; double dval = 3.14159;

Даны следующие определения объектов: complex purei( 0, 7 );

(a)Направьте значение каждого объекта в стандартный вывод.

(b)Напечатайте значение адреса pc.

С++ для начинающих

1030

(c)Напечатайте наименьшее из двух значений ival и dval, пользуясь оператором условного выражения:

ival < dval ? ival : dval

20.2. Ввод

Основное средство реализации ввода это оператор сдвига вправо (>>). Например, в

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

#include <iostream> #include <vector>

int main()

{

vector<int> ivec; int ival;

while ( cin >> ival ) ivec.push_back( ival );

// ...

int и помещается в вектор:

}

Подвыражение

cin >> ival;

читает целое число из стандартного ввода и копирует его в переменную ival. Результатом является левый операнд объект класса istream, в данном случае cin. (Как мы увидим, это позволяет сцеплять операторы ввода.)

Выражение

while ( cin >> ival )

читает последовательность значений, пока cin не станет равно false. Значение istream может быть равно false в двух случаях: достигнут конец файла (т.е. все значения из файла прочитаны успешно) или встретилось неверное значение, скажем 3.14159 (десятичная точка недопустима в целом числе), 1e-1 (буква e недопустима) или любой строковый литерал. Если вводится неверное значение, объект istream переводится в состояние ошибки и чтение прекращается. (В разделе 20.7 мы подробнее расскажем о таких состояниях.)

Есть набор предопределенных операторов ввода, принимающих аргументы любого встроенного типа, включая C-строки, а также стандартных библиотечных типов string и complex:

С++ для начинающих

1031

#include <iostream> #include <string>

int main()

{

int item_number; string item_name; double item_price;

cout << "Пожалуйста, введите item_number, item_name и price: " << endl;

cin >> item_number; cin >> item_name; cin >> item_price;

cout << "Введены значения: item# "

<<item_number << " "

<<item_name << " @$"

<<item_price << endl;

}

Вот пример выполнения этой программы:

Пожалуйста, введите item_number, item_name и price: 10247 widget 19.99

Введены значения: item# 10247 widget @$19.99

Можно ввести каждый элемент на отдельной строке. По умолчанию оператор ввода отбрасывает все разделяющие пустые символы: пробел, символ табуляции, символ перехода на новую строку, символ перевода страницы и символ возврата каретки. (О том, как отменить это поведение, см. в разделе 20.9.)

Пожалуйста, введите item_number, item_name и price: 10247

widget 19.99

Введены значения: item# 10247 widget @$19.99

При чтении ошибка iostream более вероятна, чем при записи. Если мы вводим такую последовательность:

// ошибка: item_name должно быть вторым

BuzzLightyear 10009 8.99

то инструкция

cin >> item_number;

закончится ошибкой ввода, поскольку BuzzLightyear не принадлежит типу int. При проверке объекта istream будет возвращено false, поскольку возникло состояние ошибки. Более устойчивая к ошибкам реализация выглядит так: