Я хочу написать не-Unicode, 16-битные слова в файл и прочитать их позже. Я знаю, немного манипулирования байтами, я могу сделать это в char
режим использования fstream::read()
а также fstream::write()
, Что мне нужно сделать, чтобы напрямую использовать 16-битные слова?
Например, кажется, я должен быть в состоянии сделать следующее:
basic_ofstream<uint16_t> aw;
aw.open("test.bin", ios::binary);
uint16_t c[] = {0x55aa, 0x1188};
aw.write(c, 2);
aw.close();
basic_ifstream<uint16_t> ax;
ax.open("test.bin", ios::binary);
uint16_t ui[2];
ax.read(ui, 2);
ax.close();
cout << endl << hex << unsigned(ui[0]) << " " << unsigned(ui[1]) << endl;
выход gcc 4.4:
d 0
Выход Vc ++ 10:
CCCC CCCC
Я также пытался использовать std::basic_filebuf<uint16_t>
прямой и получил те же результаты. Зачем?
Я на самом деле удивлен, что вы создали экземпляры потоков для чтения! Результат может быть определен реализацией (то есть вы можете найти поведение, описанное в документации компилятора), но, возможно, оно просто не указано (хотя и не совсем неопределенным). Я не думаю, что потоковые классы необходимы для поддержки экземпляров для других типов, чем char
а также wchar_t
немедленно, то есть без предоставления пользователем, по крайней мере, некоторых аспектов.
Стандартные потоковые классы являются шаблонами для символьного типа, но их нелегко создать для любого неподдерживаемого типа. Как минимум, вам нужно реализовать подходящий std::codecvt<int16_t, char, std::mbstate_t>
преобразование фасетов между внешним представлением в байтах и внутренним представлением. Судя по всему, две системы, которые вы попробовали, сделали разные варианты реализации по умолчанию.
std::codecvt<internT, externT, stateT>
это фасет, используемый для преобразования между внешним представлением символов и внутренним представлением символов. Потоки требуются только для поддержки char
который считается представлять байты как внешний тип externT
, Тип внутреннего символа internT
может быть любым целым типом, но преобразование должно быть определено путем реализации фасета преобразования кода. Если я правильно помню, потоки также могут предполагать, что тип состояния stateT
является std::mbstate_t
(что на самом деле несколько проблематично, потому что для этого типа не определен интерфейс!).
Если вы действительно не посвящены в создании потока ввода-вывода для вашего типа персонажа uint16_t
вы, вероятно, хотите прочитать байты, используя std::ifstream
и конвертировать их в ваш тип персонажа. Аналогично для написания персонажей. Чтобы действительно создать поток ввода-вывода, также поддерживающий форматирование, вам также потребуется ряд других аспектов (например, std::ctype<uint16_t>
, std::num_punct<uint16_t>
) и вам нужно будет построить std::locale
содержать все эти плюс несколько, которые могут быть созданы из реализации стандартной библиотеки (например, std::num_get<uint16_t>
а также std::num_put<uint16_t>
; Я думаю, что их типы итераторов подходят по умолчанию).
Когда я пробую ваш код, файл записывается, но внутри ничего нет, его размер равен 0 после закрытия. При чтении из этого файла ничего не может быть прочитано. В результате вы видите неинициализированный мусор.
Помимо использования ofstream / ifstream с символом по умолчанию, вам не обязательно полагаться на read()
а также write()
методы, потому что они не указывают, действительно ли они что-то пишут. Ссылаться на http://en.cppreference.com/w/cpp/io/basic_ostream/write для деталей об этом. Особенно это интересно
Эта функция является неотформатированной функцией вывода: она начинает выполнение с
построение объекта типа часовой, который сбрасывает связь ()
выходные буферы при необходимости и проверяет ошибки потока. После
конструкция, если объект sentry возвращает false, функция возвращает
без попытки какого-либо вывода.
Вероятно, именно поэтому в ваш файл не записывается вывод, потому что, похоже, он не предназначен для работы с любыми другими типами, кроме char или аналогичных.
Обновление: чтобы убедиться, что запись / чтение прошло успешно, проверьте бит сбой или сбой, который уже должен был указывать, что что-то пошло не так.
cout << aw.fail() << aw.bad() << "\n";
cout << ax.fail() << ax.bad() << "\n";
Оба были установлены в true, поэтому ваш реальный вопрос должен был быть: почему звонок write()
потерпеть поражение?
Я предлагаю прочитать: http://www.cplusplus.com/articles/DzywvCM9/
Отрывки:
«Проблема с этими типами заключается в том, что их размер недостаточно четко определен.
int может быть 8 байтов на одной машине, но только 4 байта на другой.
только тот, который является последовательным, является символом … который гарантированно всегда будет
1 байт. «
u16 ReadU16(istream& file)
{
u16 val;
u8 bytes[2];
file.read( (char*)bytes, 2 ); // read 2 bytes from the file
val = bytes[0] | (bytes[1] << 8); // construct the 16-bit value from those bytes
return val;
}
void WriteU16(ostream& file, u16 val)
{
u8 bytes[2];
// extract the individual bytes from our value
bytes[0] = (val) & 0xFF; // low byte
bytes[1] = (val >> 8) & 0xFF; // high byte
// write those bytes to the file
file.write( (char*)bytes, 2 );
}
Вы также можете обновить ключевое слово «typedef» для определения гарантированных — # — битных типов. В то время как немного больше кривой обучения, компиляторы Boost и C99 также определяют типы гарантированного размера. Я не уверен насчет X ++ 0x, но он слишком новый, чтобы быть переносимым.
Вы можете использовать специализации char и reinterpret_cast:
basic_ofstream<char> aw;
...
aw.write( reinterpret_cast<const char*>(i16buf), n2write*sizeof(int16_t) );
basic_ifstream<char> ax;
...
ax.read( reinterpret_cast<char*>(i16buf), n2read*sizeof(int16_t) );
«Sizeof (int16_t)» предназначен для крайних случаев, когда sizeof (int16_t) == 1 (например, процессоры DSP)
Конечно, если вам нужно читать / записывать в определенном порядке байтов, вам нужны функции преобразования в обратный порядок. Обратите внимание, что не существует стандартного способа определения порядка байтов во время компиляции.