Литмир - Электронная Библиотека
A
A

int n = 123;

string s = "123";

В первом случае число

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

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

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

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

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

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))     // чтение байтов

158
{"b":"847443","o":1}