Каков правильный способ вывода шестнадцатеричных данных в файл?

#c #hex #fstream #bmp

#c #шестнадцатеричный #fstream #bmp

Вопрос:

Я читал об этом [ostream] << hex << 0x[hex value] , но у меня есть несколько вопросов по этому поводу

(1) Я определил свой файловый поток, output , как поток шестнадцатеричного выходного файла, используя output.open("BWhite.bmp",ios::binary); , поскольку я это сделал, делает ли это hex параметр в output<< операции избыточным?

(2) Если у меня есть целочисленное значение, которое я хотел сохранить в файле, и я использовал это:

 int i = 0;
output << i;
  

буду ли я сохранен в младшем или большом конце? Изменится ли конечность в зависимости от того, на каком компьютере выполняется или компилируется программа?

Зависит ли размер этого значения от компьютера, на котором оно запущено? Нужно ли мне использовать шестнадцатеричный параметр?

(3) Существует ли способ вывода необработанных шестнадцатеричных цифр в файл? Если я хочу, чтобы файл содержал шестнадцатеричную цифру 43, что мне следует использовать?

output << 0x43 и output << hex << 0x43 оба выводят ASCII 4, затем ASCII 3.

Целью вывода этих шестнадцатеричных цифр является создание заголовка для файла .bmp.

Комментарии:

1. Конечность действительно зависит от того, на какой платформе вы выполняете программу. То же самое относится и к размеру int.

2. не output << 0x43 выводит «67»?

3. @user507078: Но на выражение output < i не влияет порядковость, поскольку выводится только текст.

Ответ №1:

Оператор форматированного вывода << предназначен именно для этого: форматированного вывода. Это для строк.

Таким образом, std::hex манипулятор stream указывает потокам выводить числа в виде строк, отформатированных как hex.

Если вы хотите выводить необработанные двоичные данные, используйте только неформатированные функции вывода, например basic_ostream::put и basic_ostream::write .

Вы могли бы вывести значение типа int следующим образом:

 int n = 42;
output.write(amp;n, sizeof(int));
  

Порядковый номер этого вывода будет зависеть от архитектуры. Если вы хотите иметь больше контроля, я предлагаю следующее:

 int32_t n = 42;
char data[4];
data[0] = static_cast<char>(n amp; 0xFF);
data[1] = static_cast<char>((n >> 8) amp; 0xFF);
data[2] = static_cast<char>((n >> 16) amp; 0xFF);
data[3] = static_cast<char>((n >> 24) amp; 0xFF);
output.write(data, 4);
  

В этом примере 32-разрядное целое число будет выводиться в виде младшего порядкового номера независимо от порядкового номера платформы. Однако будьте осторожны с обратным преобразованием, если char оно подписано.

Комментарии:

1. Это целочисленные типы данных явного размера, доступные из заголовка <cstdint> в большинстве последних компиляторов. Я использовал int32_t, потому что это гарантирует, что его ширина составляет 4 байта, чего не делает только int.

2. Я тестировал с использованием short вместо int, и кажется, что файл содержит замененные байты. например, для сокращения 0x01f7 , используя output.write(reinterpret_cast< char* >(amp;n), sizeof(n)); , файл содержит 0xf701

Ответ №2:

Вы говорите

«Есть ли способ вывести необработанные шестнадцатеричные цифры в файл? Если я хочу, чтобы файл содержал шестнадцатеричную цифру 43, что мне следует использовать? «

«Необработанные шестнадцатеричные цифры» будут зависеть от интерпретации, которую вы выполняете для набора битов. Рассмотрим следующее:

  Binary  :    0 1 0 0 1 0 1 0
 Hex     :    4 A
 Octal   :    1 1 2
 Decimal :    7 4
 ASCII   :    J
  

Все вышесказанное представляет одну и ту же числовую величину, но мы интерпретируем ее по-разному.

Таким образом, вам может просто понадобиться сохранить данные в двоичном формате, то есть в точном битовом шаблоне, который представлен числом.

ПРАВКА1

Когда вы открываете файл в текстовом режиме и вводите в него число, скажем, при записи 74 (как в приведенном выше примере) оно будет сохранено в виде двух символов ASCII '7' и '4' . Чтобы избежать этого, откройте файл в двоичном режиме ios::binary и запишите его с помощью write () . Проверьте http://courses.cs.vt.edu /~cs2604/fall00/binio.html#записать

Ответ №3:

Целью вывода этих шестнадцатеричных цифр является создание заголовка для файла .bmp.

Похоже, у вас большое неправильное представление о том, как работают файлы.

Операторы потока << генерируют текст (вывод, понятный человеку). Формат файла .bmp — это двоичный формат, который не читается человеком (будет, но это не приятно, и я бы не стал читать его без инструментов).

Что вы действительно хотите сделать, это сгенерировать двоичный вывод и поместить его в файл:

 char   x = 0x43;
output.write(amp;x, sizeof(x));
  

При этом в выходной поток будет записан один байт данных с шестнадцатеричным значением 0x43. Это двоичное представление, которое вы хотите.

буду ли я сохранен в младшем или большом конце? Изменится ли конечность в зависимости от того, на каком компьютере выполняется или компилируется программа?

Ни то, ни другое; вы снова выводите текст (не двоичные данные).

 int i = 0;
output.write(reinterpret_cast<char*>(amp;i), sizeof(i)); // Writes the binary representation of i
  

Здесь вам действительно нужно беспокоиться о порядковости (и размере) целочисленного значения, и это будет варьироваться в зависимости от оборудования, на котором вы запускаете свое приложение. Для значения 0 не стоит сильно беспокоиться о порядковости, но вам следует беспокоиться о размере целого числа.

Я бы вставил некоторые утверждения в свой код, чтобы подтвердить, что архитектура подходит для кода. Тогда пусть люди беспокоятся о том, не соответствует ли их архитектура требованиям:

 int test = 0x12345678;
assert((sizeof(test) * CHAR_BITS == 32) amp;amp; "BMP uses 32 byte ints");
assert((((char*)amp;test)[0] == 0x78) amp;amp; "BMP uses little endian");
  

Существует семейство функций, которые помогут вам с порядком следования и размером.

http://www.gnu.org/s/hello/manual/libc/Byte-Order.html

Функция: uint32_t htonl (uint32_t hostlong)
Эта функция преобразует целое число hostlong uint32_t из порядка байтов хоста в сетевой порядок байтов.

 // Writing to a file
uint32_t hostValue = 0x12345678;
uint32_t network   = htonl(hostValue);
output.write(amp;network, sizeof(network));

// Reading from a file
uint32_t network;
output.read(amp;network, sizeof(network);
uint32_t hostValue = ntohl(network);    // convert back to platform specific value.

// Unfortunately the BMP was written with intel in-mind
// and thus all integers are in liitle-endian.
// network bye order (as used by htonl() and family) is big endian.
// So this may not be much us to you.
  

И последнее. Когда вы открываете файл в двоичном формате, output.open("BWhite.bmp",ios::binary) он ничего не делает для потоковой передачи, кроме того, как он обрабатывает end of line sequence . Когда файл находится в двоичном формате, выходные данные не изменяются (то, что вы помещаете в поток, записывается в файл). Если вы оставляете поток в текстовом режиме, то символы ‘ n’ преобразуются в последовательность конца строки (специфичный для операционной системы набор символов, которые определяют конец строки). Поскольку вы пишете двоичный файл, вы определенно не хотите никакого вмешательства в символы, которые вы записываете, поэтому двоичный формат является правильным. Но это не влияет ни на какие другие операции, которые вы выполняете в потоке.

Комментарии:

1. Подождите … значит, мне не нужно беспокоиться о бесконечности, если я использую output.write(), как вы делали? Как? Что, если мой компьютер сохранит их в конце строки, а чей-то другой компьютер попытается прочитать их в конце строки? Кроме того, спасибо за отличный ответ!

2. Также: если целые числа изменяются по размеру, означает ли это, что размер записей данных в заголовке также изменяется?

3. @jwaffe: Вам ДЕЙСТВИТЕЛЬНО нужно беспокоиться о последовательности. Я говорил, что способ, которым вы делали это с operator << , был настолько неправильным, что это не имеет значения, поскольку выводится текст.

4. Прямо под вашим кодом для output.write(), вы сказали Here you do not need to worry about endianess (and size) of the integer value... . Это то, что я имею в виду. Вы хотите сказать, что мне не нужно беспокоиться о порядковости, только если значение равно нулю?

5. @jwaffe: sizeof (int) не определен для какого-либо конкретного размера, поэтому вам нужно беспокоиться об этом. Поместите assert(sizeof(int) == 4); в свой код, чтобы убедиться, что он не будет работать при переносе его на новую платформу, где это условие нарушается. Размер файла .bmp, я уверен, зафиксирован на очень определенном размере.