Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
• Режим trunc может быть установлен, только если устанавливается также режим out.
• Режим app может быть установлен, только если не установлен режим trunc. Если режим app установлен, файл всегда открывается в режиме вывода, даже если это не было указано явно.
• По умолчанию файл, открытый в режиме out, усекается, даже если не задан режим trunc. Чтобы сохранить содержимое файла, открытого в режиме out, необходимо либо задать также режим app, тогда можно будет писать только в конец файла, либо задать также режим in, тогда файл откроется и для ввода, и для вывода. Использование того же файла для ввода и вывода рассматривается в разделе 17.5.3.
• Режимы ate и binary могут быть установлены для объекта файлового потока любого типа и в комбинации с любыми другими режимами.
Для каждого типа файлового потока задан режим файла по умолчанию, который используется в случае, если режим не задан. Файлы, связанные с потоками типа ifstream, открываются в режиме in; файлы, связанные с потоками типа ofstream, открываются в режиме out; а файлы, связанные с потоками типа fstream, открываются в режимах in и out.
Открытие файла в режиме out удаляет существующие данныеПо умолчанию при открытии потока типа ofstream содержимое файла удаляется. Единственный способ воспрепятствовать удалению данных файла подразумевает установку режима app:
// file1 усекается в каждом из следующих случаев
ofstream out("file1"); // out и trunc установлены неявно
ofstream out2("file1", ofstream::out); // trunc установлен неявно
ofstream out3("file1", ofstream::out | ofstream::trunc);
// для сохранения содержимого файла следует явно задать режим app
ofstream app("file2", ofstream::app); // out установлен неявно
ofstream app2("file2", ofstream::out | ofstream::app);
Единственный способ сохранить существующие данные в файле, открытом потоком типа ofstream, — это явно установить режим app или in.
Режим файла устанавливается при каждом вызове функции open()Режим файла некоего потока может изменяться при каждом открытии файла.
ofstream out; // режим файла не установлен
out.open("scratchpad"); // неявно заданы режимы out и trunc
out.close(); // out закрыт, его можно использовать для другого файла
out.open("precious", ofstream::app); // режимы out и app
out.close();
Первый вызов функции open() не задает режим вывода явно; этот файл неявно открывается в режиме out. Как обычно, режим out подразумевает также режим trunc. Поэтому файл scratchpad, расположенный в текущем каталоге, будет усечен. Когда открывается файл precious, задается режим добавления. Все данные остаются в файле, а запись осуществляется в конец файла.
Режим файла устанавливается при каждом вызове функции open() явно или неявно. Когда режим не устанавливается явно, используется значение по умолчанию.
Упражнения раздела 8.2.2Упражнение 8.7. Пересмотрите программу книжного магазина из предыдущего раздела так, чтобы вывод записывался в файл. Передайте имя этого файла как второй аргумент функции main().
Упражнение 8.8. Пересмотрите программу из предыдущего упражнения так, чтобы добавить ее вывод в заданный файл. Запустите программу для того же выходного файла по крайней мере дважды и удостоверьтесь, что данные сохраняются.
8.3. Строковые потоки
Заголовок sstream определяет три типа, поддерживающие операции ввода-вывода в оперативной памяти; эти типы обеспечивают чтение или запись в строку, как будто она является потоком ввода-вывода.
Объект класса istringstream читает строку, объект класса ostringstream записывает строку, а объект класса stringstream читает и записывает строку. Подобно типам заголовка fstream, типы, определенные в заголовке sstream, происходят от типов, используемых заголовком iostream. Кроме унаследованных операций, типы, определенные в заголовке sstream, имеют дополнительные члены для работы со строками, связанными с потоком. Эти операции перечислены в табл. 8.5. Они могут быть выбраны для объектов класса stringstream, строковых потоков (string stream), но не других типов ввода-вывода.
Обратите внимание на то, что хотя заголовки fstream и sstream имеют общий интерфейс к заголовку iostream, никакой другой взаимосвязи у них нет. В частности, нельзя использовать функции open() и close() для объектов класса stringstream, а функцию str() нельзя использовать для объектов класса fstream.
Таблица 8.5. Операции, специфические для класса stringstream
sstream strm; strm — несвязанный объект класса stringstream. sstream — это один из типов, определенных в заголовке sstream sstream strm(s); sstream содержит копию строки s. Этот конструктор является явным (см. раздел 7.5.4). strm.str() Возвращает копию строки, которую хранит объект strm strm.str(s) Копирует строку s в объект strm. Возвращает тип void8.3.1. Использование класса istringstream
Класс istringstream зачастую используют тогда, когда некую работу следует выполнить со всей строкой и другую работу с отдельными словами в пределах строки.
Предположим, например, что имеется файл, содержащий список людей и номеров их телефонов. У одних людей есть только один номер, а у других несколько — домашний телефон, рабочий, мобильный и т.д. Наш исходный файл может выглядеть следующим образом:
morgan 2015552368 8625550123
drew 9735550130
lee 6095550132 2015550175 8005550000
Каждая запись в этом файле начинается с имени, затем следует один или несколько номеров телефонов. Для начала определим простой класс, представляющий исходные данные:
// по умолчанию члены являются открытыми; см. раздел 1.2
struct PersonInfo {
string name;
vector<string> phones;
};
Один член класса PersonInfo будет представлять имя человека, а вектор будет содержать переменное количество его номеров телефонов.
Наша программа будет читать файл данных и создавать вектор объекта класса PersonInfo. Каждый элемент вектора будет соответствовать одной записи в файле. Ввод обрабатывается в цикле, который читает запись, а затем извлекает имя и номера телефона каждого человека:
string line, word; // будут содержать строку и слово из ввода
vector<PersonInfо> people; // будет содержать все записи из ввода
// читать ввод по строке за раз, пока не встретится конец файла
// (или другая ошибка)
while (getline(cin, line)) {
PersonInfo info; // создать объект для содержания данных записи
istringstream record(line); // связать запись с читаемой строкой
record >> info.name; // читать имя
while (record >> word) // читать номер телефона
info.phones.push_back(word); // и сохранить их
people.push_back(info); // добавить эту запись в people
}
Здесь для чтения всей записи со стандартного устройства ввода используется функция getline(). Если вызов функции getline() успешен, то переменная line будет содержать запись из входного файла. В цикле while определяется локальный объект PersonInfo для содержания данных из текущей записи.
Затем с только что прочитанной строкой связывается поток istringstream. Теперь для чтения каждого элемента в текущей записи можно использовать оператор ввода класса istringstream. Сначала читается имя, затем следует цикл while, читающий номера телефонов данного человека.
Внутренний цикл while завершается, когда все данные в строке прочитаны. Этот цикл работает аналогично другим, написанным для чтения из объекта cin. Различие только в том, что этот цикл читает данные из строки, а не со стандартного устройства ввода. Когда строка прочитана полностью, встретившийся "конец файла" свидетельствует о том, что следующая операция ввода в объект record потерпит неудачу.