<< Пред.           стр. 101 (из 121)           След. >>

Список литературы по разделу

 
  cout << "Вхождения: " << "\n"
  << search << endl;
 
  return 0;
 }
 После компиляции и запуска программа выводит следующее:
 
 Вхождения:
 <10> rosebud
 <11,3> <11,8> <14,2> <34,6> <49,7> <67,5>
 <81,2> <82,3> <91,4> <97,8>
 
 Полученный результат сохранен в файле output. Далее мы определим оператор ввода, с помощью которого прочитаем данные из этого файла.
 Упражнение 20.7
 Дано определение класса Date:
 class Date {
 public:
  // ...
 private:
  int month, day, year;
 };
 Напишите перегруженный оператор вывода даты в формате:
 (a)
 // полное название месяца
 September 8th, 1997
 (b)
 9 / 8 / 97
 (c) Какой формат лучше? Объясните.
 (d) Должен ли оператор вывода Date быть функцией-другом? Почему?
 Упражнение 20.8
 Определите оператор вывода для следующего класса CheckoutRecord:
 class CheckoutRecord { // запись о выдаче
 public:
  // ...
 private:
  double book_id; // идентификатор книги
  string title; // название
  Date date_borrowed; // дата выдачи
  Date date_due; // дата возврата
  pair borrower; // кому выдана
  vector pair wait_list; // очередь на книгу
 };
 20.5. Перегрузка оператора ввода
 Перегрузка оператора ввода (>>) похожа на перегрузку оператора вывода, но, к сожалению, возможностей для ошибок гораздо больше. Вот, например, его реализация для класса WordCount:
 #include
 #include "WordCount.h"
 
 /* необходимо модифицировать определение класса WordCount, чтобы
  оператор ввода был другом
  class WordCount {
  friend ostream& operator<<( ostream&, const WordCount& );
  friend istream& operator>>( istream&, const WordCount& );
 */
 
 istream&
 operator >>( istream &is, WordCount &wd )
 {
  /* формат хранения объекта WordCount:
  * <2> строка
  * <7,3> <12,36>
  */
 
  int ch;
 
  /* прочитать знак '<'. Если его нет,
  * перевести поток в ошибочное состояние и выйти
  */
  if ((ch = is.get()) != '<' )
  {
  // is.setstate( ios_base::badbit );
  return is;
  }
 
  // прочитать длину
  int occurs;
  is >> occurs;
 
  // читать до обнаружения >; ошибки не контролируются
  while ( is && (ch = is.get()) != '>' ) ;
 
  is >> wd._word;
 
  // прочитать позиции вхождений;
  // каждая позиция имеет формат: < строка, колонка >
  for ( int ix = 0; ix < occurs; ++ix )
  {
  int line, col;
  // извлечь значения
  while (is && (ch = is.get())!= '<' ) ;
  is >> line;
 
  while (is && (ch = is.get())!= ',' ) ;
  is >> col;
 
  while (is && (ch = is.get())!= '>' ) ;
 
  wd._occurList.push_back( Location( line, col ));
  }
  return is;
 }
 На этом примере показан целый ряд проблем, имеющих отношение к возможным ошибочным состояниям входного потока:
 поток, чтение из которого невозможно из-за неправильного формата, переводится в состояние fail:
 is.setstate( ios_base::failbit );
 операции вставки и извлечения из потока, находящегося в ошибочном состоянии, не работают:
 while (( ch = is.get() ) != lbrace)
 Инструкция зациклится, если объект istream будет находиться в ошибочном состоянии. Поэтому перед каждым обращением к get() проверяется отсутствие ошибки:
 // проверить, находится ли поток "is" в "хорошем" состоянии
 while ( is && ( ch = is.get() ) != lbrace)
 Если объект istream не в “хорошем” состоянии, то его значение будет равно false. (О состояниях потока мы расскажем в разделе 20.7.)
 Данная программа считывает объект класса WordCount, сохраненный оператором вывода из предыдущего раздела:
 #include
 #include "WordCount.h"
 
 int main()
 {
  WordCount readIn;
 
  // operator>>( cin, readIn )
  cin >> readIn;
 
  if ( !cin ) {
  cerr << "Ошибка ввода WordCount" << endl;
  return -1;
  }
 
  // operator<<( cout, readIn )
  cout << readIn << endl;
 }
 Выводится следующее:
 
 <10> rosebud
 <11,3> <11,8> <14,2> <34,6> <49,7> <67,5>
 <81,2> <82,3> <91,4> <97,8>
 
 Упражнение 20.9
 Оператор ввода класса WordCount сам читает объекты класса Location. Вынесите этот код в отдельный оператор ввода класса Location.
 Упражнение 20.10
 Реализуйте оператор ввода для класса Date из упражнения 20.7 в разделе 20.4.
 Упражнение 20.11
 Реализуйте оператор ввода для класса CheckoutRecord из упражнения 20.8 в разделе 20.4.
 20.6. Файловый ввод/вывод
 Если программе необходимо работать с файлом, то следует включить в нее заголовочный файл fstream (который в свою очередь включает iostream):
 #include
 Если файл будет использоваться только для вывода, мы определяем объект класса ofstream. Например:
 ofstream outfile( "copy.out", ios::base::out );
 Передаваемые конструктору аргументы задают имя открываемого файла и режим открытия. Файл типа ofstream может быть открыт либо – по умолчанию – в режиме вывода (ios_base::out), либо в режиме дозаписи (ios_base::app). Такое определение файла outfile2 эквивалентно приведенному выше:
 // по умолчанию открывается в режиме вывода
 ofstream outfile2( "copy.out" );
 Если в режиме вывода открывается существующий файл, то все хранившиеся в нем данные пропадают. Если же мы хотим не заменить, а добавить данные, то следует открывать файл в режиме дозаписи: тогда новые данные помещаются в конец. Если указанный файл не существует, то он создается в любом режиме.
 Прежде чем пытаться прочитать из файла или записать в него, нужно проверить, что файл был успешно открыт:
 if ( ! outfile ) { // открыть файл не удалось
  cerr << "не могу открыть "copy.out" для записи\n";
  exit( -1 );
 }
 Класс ofstream является производным от ostream. Все определенные в ostream операции применимы и к ofstream. Например, инструкции
 char ch = ' ';
 outFile.put( '1' ).put( ')' ).put( ch );
 outFile << "1 + 1 = " << (1 + 1) << endl;
 выводят в файл outFile последовательность символов:
 
 1) 1 + 1 = 2
 
 Следующая программа читает из стандартного ввода символы и копирует их в стандартный вывод:
 #include
 
 int main()
 {
  // открыть файл copy.out для вывода
  ofstream outFile( "copy.out" );
 
  if ( ! outFile ) {
  cerr << "Не могу открыть 'copy.out' для вывода\n";
  return -1;
  }
 
  char ch;
  while ( cin.get( ch ) )
  outFile.put( ch );
 }
 К объекту класса ofstream можно применять и определенные пользователем экземпляры оператора вывода. Данная программа вызывает оператор вывода класса WordCount из предыдущего раздела:
 #include
 #include "WordCount.h"
 
 int main()
 {
  // открыть файл word.out для вывода
  ofstream oFile( "word.out" );
  // здесь проверка успешности открытия ...
 
  // создать и вручную заполнить объект WordCount
  WordCount artist( "Renoir" );
  artist.found( 7, 12 ); artist.found( 34, 18 );
 
  // вызывается оператор <<(ostream&, const WordCount&);
  oFile << artist;
 }
 Чтобы открыть файл только для чтения, применяется объект класса ifstream, производного от istream. Следующая программа читает указанный пользователем файл и копирует его содержимое на стандартный вывод:
 #include
 #include
 
 int main()
 {
  cout << "filename: ";
  string file_name;
 
  cin >> file_name;
 
  // открыть файл для ввода
  ifstream inFile( file_name.c_str() );
 
  if ( !inFile ) {
  cerr << "не могу открыть входной файл: "
  << file_name << " -- аварийный останов!\n";
  return -1;
  }
 
  char ch;
  while ( inFile.get( ch ))
  cout.put( ch );
 }
 Программа, показанная ниже, читает наш текстовый файл alice_emma, фильтрует его с помощью функции filter_string() (см. раздел 20.2.1, где приведены текст этой функции и содержимое файла), сортирует строки, удаляет дубликаты и записывает результат на стандартный вывод:
 #include
 #include
 #include
 #include
 
 template
 void filter_string( InputIterator first, InputIterator last,
  string filt_elems = string("\",?."))
 {
  for ( ; first != last; first++ )
  {
  string::size_type pos = 0;
  while (( pos = (*first).find_first_of( filt_elems, pos ))
  != string::npos )
  (*first).erase( pos, 1 );
  }
 }
 
 int main()
 {
  ifstream infile( "alice_emma" );
 
  istream_iterator ifile( infile );
  istream_iterator eos;
 
  vector< string > text;
  copy( ifile, eos, inserter( text, text.begin() ));
 
  string filt_elems( "\",.?;:" );
  filter_string( text.begin(), text.end(), filt_elems );
 
  vector::iterator iter;
 
  sort( text.begin(), text.end() );
  iter = unique( text.begin(), text.end() );
  text.erase( iter, text.end() );
 
  ofstream outfile( "alice_emma_sort" );
 
  iter = text.begin();
  for ( int line_cnt = 1; iter != text.end();
  ++iter, ++line_cnt )
  {
  outfile << *iter << " ";
  if ( ! ( line_cnt % 8 ))
  outfile << '\n';
  }
  outfile << endl;
 }
 После компиляции и запуска программа выводит следующее:
 
 A Alice Daddy Emma Her I Shyly a
 alive almost asks at beautiful bird blows but
 creature fiery flight flowing hair has he her
 him in is it like long looks magical
 mean more no red same says she shush
 such tell tells the there through time to
 untamed wanting when wind
 
 Объекты классов ofstream и ifstream разрешено определять и без указания имени файла. Позже к этому объекту можно присоединить файл с помощью функции-члена open():
 ifstream curFile;
 // ...
 curFile.open( filename.c_str() );
 if ( ! curFile ) // открытие успешно?
  // ...
 Чтобы закрыть файл (отключить от программы), вызываем функцию-член close():
 #include
 
 const int fileCnt = 5;
 string fileTabl[ fileCnt ] = {
  "Melville", "Joyce", "Musil", "Proust", "Kafka"
 };
 
 int main()
 {
  ifstream inFile; // не связан ни с каким файлом
 
  for ( int ix = 0; ix < fileCnt; ++ix )

<< Пред.           стр. 101 (из 121)           След. >>

Список литературы по разделу