Программирование на C / C++ / Ален И. Голуб. Правила программирования на Си и Си++ [pdf]
.pdfС++ для начинающих |
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, поскольку возникло состояние ошибки. Более устойчивая к ошибкам реализация выглядит так: