Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 112 113 114 115 116 117 118 119 120 ... 337
сожалению, в этом случае каждый новый объект класса Month содержал бы все показания всех предшествующих месяцев текущего года. Для того чтобы считывать данные с помощью инструкции is>>m, нам нужен совершенно новый объект класса Month. Проще всего поместить определение объекта m в цикл так, чтобы он инициализировался на каждой итерации.

В качестве альтернативы можно было бы сделать так, чтобы функция operator>>(istream& is, Month& m) перед считыванием в цикле присваивала бы объекту m пустой объект.

Month m;

while (is >> m) {

  y.month[m.month] = m;

  m = Month(); // "Повторная инициализация" объекта m

}

Попробуем применить это.

// открываем файл для ввода:

cout << "Пожалуйста, введите имя файла для ввода n";

string name;

cin >> name;

ifstream ifs(name.c_str());

if (!ifs) error(" невозможно открыть файл для ввода ",name);

ifs.exceptions(ifs.exceptions()|ios_base::badbit); // генерируем bad()

// открываем файл для вывода:

cout << "Пожалуйста, введите имя файла для ввода n";

cin >> name;

ofstream ofs(name.c_str());

if (!ofs) error(" невозможно открыть файл для ввода ",name);

// считываем произвольное количество объектов класса Year:

vector<Year> ys;

while(true) {

  Year y; // объект класса Year каждый раз очищается

  if (!(ifs>>y)) break;

  ys.push_back(y);

}

cout << " считано " << ys.size() << " записей по годам.n";

for (int i = 0; i<ys.size(); ++i) print_year(ofs,ys[i]);

Функцию print_year() мы оставляем в качестве упражнения. 

10.11.3. Изменение представления

Для того чтобы оператор >> класса Month работал, необходимо предусмотреть способ для ввода символьных представлений месяца. Для симметрии мы описываем способ сравнения с помощью символьного представления. Было бы слишком утомительно писать инструкции if, подобные следующей:

if (s=="jan")

  m = 1;

else if (s=="feb")

  m = 2;

...

Это не просто утомительно; таким образом мы встраиваем названия месяцев в код. Было бы лучше занести их в таблицу, чтобы основная программа оставалась неизменной, даже если мы изменим символьное представление месяцев. Мы решили представить входную информацию в виде класса vector<string>, добавив к нему функцию инициализации и просмотра.

vector<string> month_input_tbl; // month_input_tbl[0]=="jan"

void init_input_tbl(vector<string>& tbl)

// инициализирует вектор входных представлений

{

  tbl.push_back("jan");

  tbl.push_back("feb");

  tbl.push_back("mar");

  tbl.push_back("apr");

  tbl.push_back("may");

  tbl.push_back("jun");

  tbl.push_back("jul");

  tbl.push_back("aug");

  tbl.push_back("sep");

  tbl.push_back("oct");

  tbl.push_back("nov");

  tbl.push_back("dec");

}

int month_to_int(string s)

// Является ли строка s названием месяца? Если да, то возвращаем ее

// индекс из диапазона [0:11], в противном случае возвращаем –1

{

  for (int i=0; i<12; ++i) if (month_input_tbl[i]==s) return i;

  return –1;

}

На всякий случай заметим, что стандартная библиотека С++ предусматривает более простой способ решения этой задачи. См. тип map<string,int> в разделе 21.6.1.

Если мы хотим вывести данные, то должны решить обратную задачу. У нас есть представление месяца с помощью чисел int, и мы хотели бы представить их в символьном виде. Наше решение очень простое, но вместо использования таблицы перехода от типа string к типу int мы теперь используем таблицу перехода от типа int к типу string.

vector<string> month_print_tbl; // month_print_tbl[0]=="January"

void init_print_tbl(vector<string>& tbl)

// инициализируем вектор представления для вывода

{

  tbl.push_back("January");

  tbl.push_back("February");

  tbl.push_back("March");

  tbl.push_back("April");

  tbl.push_back("May");

  tbl.push_back("June");

  tbl.push_back("July");

  tbl.push_back("August");

  tbl.push_back("September");

  tbl.push_back("October");

  tbl.push_back("November");

  tbl.push_back("December");

}

string int_to_month(int i)

// месяцы [0:11]

{

  if (i<0 || 12<=i) error("Неправильный индекс месяца.");

  return month_print_tbl[i];

}

Для того чтобы этот подход работал, необходимо где-то вызвать функции инициализации, такие как указаны в начале функции main().

// первая инициализация таблиц представлений:

init_print_tbl(month_print_tbl);

init_input_tbl(month_input_tbl);

 

 Итак, действительно ли вы прочитали все фрагменты кода и пояснения к ним? Или ваши глаза устали, и вы перешли сразу в конец главы? Помните, что самый простой способ научиться писать хорошие программы — читать много чужих программ. Хотите — верьте, хотите — нет, но методы, использованные в описанном примере, просты, хотя и не тривиальны, и требуют объяснений. Ввод данных — фундаментальная задача. Правильная разработка циклов ввода (с корректной инициализацией каждой использованной переменной) также очень важна. Не меньшее значение имеет задача преобразования одного представления в другое. Иначе говоря, вы должны знать такие методы. Остается лишь выяснить, насколько хорошо вы усвоили эти методы и не упустили ли из виду важные факты.

Задание

1. Разработайте программу, работающую с точками (см. раздел 10.4). Начните с определения типа данных Point, имеющего два члена — координаты x и y.

2. Используя код и обсуждение из раздела 10.4, предложите пользователю ввести семь пар (x,y). После ввода данных запишите их в вектор объектов класса Point с именем original_points.

3. Выведите на печать данные из объекта original_points, чтобы увидеть, как они выглядят.

4. Откройте поток ofstream и выведите все точки в файл mydata.txt. В системе Windows для облегчения просмотра данных с помощью простого текстового редактора (например, WordPad) лучше использовать расширение файла .txt.

5. Закройте поток ofstream, а затем откройте поток ifstream для файла mydata.txt. Введите данные из файла mydata.txt и запишите их в новый вектор с именем processed_points.

6. Выведите на печать данные из обоих векторов.

7. Сравните эти два вектора и выведите на печать сообщение Что-то не так !, если количество элементов или значений элементов в векторах не совпадает.

Контрольные вопросы

1. Насколько разнообразными являются средства ввода и вывода у современных компьютеров?

2. Что делает поток istream?

3. Что делает поток ostream?

4. Что такое файл?

5. Что такое формат файла?

6. Назовите четыре разных типа устройств для ввода и вывода данных из программ.

7. Перечислите четыре этапа чтения файла.

8. Перечислите четыре этапа записи файлов.

9. Назовите и определите четыре состояния потоков.

10. Обсудите возможные способы решения следующих задач ввода.

 10.1. Пользователь набрал значение, выходящее за пределы допустимого

1 ... 112 113 114 115 116 117 118 119 120 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий