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