Чтение онлайн

на главную - закладки

Жанры

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

В данном случае причиной ошибки стала опечатка.

Обычно, когда вы пытаетесь открыть несуществующий файл, операционная система создает новый файл для вывода, но, к счастью, она не делает этого, когда вы обращаетесь к несуществующему файлу для ввода.

ofstream ofs("no-such-file"); // создает новый файл no-such-file

ifstream ifs("no-file-of-this-name"); // ошибка: поток ifs не нахо-

// дится в состоянии good

11.3.2. Бинарные файлы

В памяти мы можем представить значение 123 как целое или как строку. Рассмотрим пример.

int n = 123;

string s = "123";

В

первом случае число
123
интерпретируется как (двоичное) число. Объем памяти, который оно занимает, совпадает с объемом памяти, который занимает любое другое целое число (
4
байта, т.е.
32
бита на персональном компьютере). Если вместо числа
123
мы выберем число
12345
, то оно по-прежнему будет занимать те же самые четыре байта. Во втором варианте значение
123
хранится как строка из трех символов. Если мы выберем строку
"12345"
, то для ее хранения нам потребуются пять символов (плюс накладные расходы памяти на управление объектом класса
string
). Проиллюстрируем сказанное, используя обычные десятичное и символьное представления, а не двоичное, как в памяти компьютера.

Когда мы используем символьное представление, то какой-то символ должен служить признаком конца числа, так же как на бумаге, когда мы записываем одно число 123456 и два числа 123 456. На бумаге для разделения чисел мы используем пробел. То же самое можно сделать в памяти компьютера.

Разница между хранением двоичного представления фиксированного размера (например, в виде типа

int
) и символьного представления переменного размера (например, в виде типа
string
) проявляется и при работе с файлами. По умолчанию потоки
iostream
работают с символьными представлениями; иначе говоря, поток
istream
считывает последовательность символов и превращает их в объект заданного типа. Поток
ostream
принимает объект заданного типа и преобразует их в последовательность записываемых символов. Однако можно потребовать, чтобы потоки
istream
и
ostream
просто копировали байты из файла в файл. Такой ввод-вывод называется двоичным (binary I/O). В этом случае файл необходимо открыть в режиме
ios_base::binary
. Рассмотрим пример, в котором считываются и записываются двоичные файлы, содержащие целые числа. Главные сроки, предназначенные для обработки двоичных файлов, объясняются ниже.

int main

{

// открываем поток istream для двоичного ввода из файла:

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

string name;

cin >> name;

ifstream ifs(name.c_str,ios_base::binary); // примечание: опция

// binary сообщает потоку, чтобы он ничего не делал

// с байтами

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

// открываем поток ostream для двоичного вывода в файл:

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

cin >> name;

ofstream ofs(name.c_str,ios_base::binary); // примечание: опция

// binary сообщает потоку, чтобы он ничего не делал

// с байтами

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

vector<int> v;

// чтение из бинарного файла:

int i;

while (ifs.read(as_bytes(i),sizeof(int))) // примечание:

// читаем байты

v.push_back(i);

// ...что-то
делаем с вектором v...

// записываем в двоичный файл:

for(int i=0; i<v.size; ++i)

ofs.write(as_bytes(v[i]),sizeof(int)); // примечание:

// запись байтов

return 0;

}

Мы открыли эти файлы с помощью опции

ios_base::binary
.

ifstream ifs(name.c_str, ios_base::binary);

ofstream ofs(name.c_str, ios_base::binary);

В обоих вариантах мы выбрали более сложное, но часто более компактное двоичное представление. Если мы перейдем от символьно-ориентированного ввода-вывода к двоичному, то не сможем использовать обычные операторы ввода и вывода

>>
и
<<
. Эти операторы преобразуют значения в последовательности символов, руководствуясь установленными по умолчанию правилами (например, строка
"asdf"
превращается в символы
a
,
s
,
d
,
f
, а число
123
превращается в символы
1
,
2
,
3
). Если вы не хотите работать с двоичным представлением чисел, достаточно ничего не делать и использовать режим, заданный по умолчанию. Мы рекомендуем применять опцию
binary
, только если вы (или кто-нибудь еще) считаете, что так будет лучше. Например, с помощью опции
binary
можно сообщить потоку, что он ничего не должен делать с байтами.

А что вообще мы могли бы сделать с типом

int
? Очевидно, записать его в память размером четыре байта; иначе говоря, мы могли бы обратиться к представлению типа int в памяти (последовательность четырех байтов) и записать эти байты в файл. Позднее мы могли бы преобразовать эти байты обратно в целое число.

ifs.read(as_bytes(i),sizeof(int)) // чтение байтов

ofs.write(as_bytes(v[i]),sizeof(int)) // запись байтов

Функция

write
потока
ostream
и функция
read
потока
istream
принимают адрес (с помощью функции
as_bytes
) и количество байтов (символов), полученное с помощью оператора
sizeof
. Этот адрес должен ссылаться на первый байт в памяти, хранящей значение, которое мы хотим прочитать или записать. Например, если у нас есть объект типа
int
со значением
1234
, то мы могли бы получить четыре байта (используя шестнадцатеричную систему обозначений) —
00
,
00
,
04
,
d2
:

Функция

as_bytes
позволяет получить адрес первого байта объекта. Ее определение выглядит так (некоторые особенности языка, использованные здесь, будут рассмотрены в разделах 17.8 и 19.3):

template<class T>

char* as_bytes(T& i) // рассматривает объект T как последовательность

// байтов

{

void* addr = &i; // получаем адрес первого байта

// памяти, использованной для хранения объекта

return static_cast<char*>(addr); // трактуем эту память как байты

}

Небезопасное преобразование типа с помощью оператора static_cast необходимо для того, чтобы получить переменную в виде совокупности байтов. Понятие адреса будет подробно изучено в главах 17 и 18. Здесь мы просто показываем, как представить любой объект, хранящийся в памяти, в виде совокупности байтов, чтобы прочитать или записать его с помощью функций

read
и
write
.

Поделиться:
Популярные книги

Ведьма

Резник Юлия
Любовные романы:
современные любовные романы
эро литература
8.54
рейтинг книги
Ведьма

Комбинация

Ланцов Михаил Алексеевич
2. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Комбинация

Неудержимый. Книга XI

Боярский Андрей
11. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XI

Большая игра

Ланцов Михаил Алексеевич
4. Иван Московский
Фантастика:
альтернативная история
5.00
рейтинг книги
Большая игра

Хозяйка Междуречья

Алеева Елена
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Хозяйка Междуречья

Возвышение Меркурия. Книга 3

Кронос Александр
3. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 3

Восьмое правило дворянина

Герда Александр
8. Истинный дворянин
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восьмое правило дворянина

Идеальный мир для Лекаря 6

Сапфир Олег
6. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 6

Жена моего брата

Рам Янка
1. Черкасовы-Ольховские
Любовные романы:
современные любовные романы
6.25
рейтинг книги
Жена моего брата

Генерал Империи

Ланцов Михаил Алексеевич
4. Безумный Макс
Фантастика:
альтернативная история
5.62
рейтинг книги
Генерал Империи

Купеческая дочь замуж не желает

Шах Ольга
Фантастика:
фэнтези
6.89
рейтинг книги
Купеческая дочь замуж не желает

Академия

Сай Ярослав
2. Медорфенов
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Академия

Светлая ведьма для Темного ректора

Дари Адриана
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Светлая ведьма для Темного ректора

Хроники разрушителя миров. Книга 8

Ермоленков Алексей
8. Хроники разрушителя миров
Фантастика:
фэнтези
5.00
рейтинг книги
Хроники разрушителя миров. Книга 8