2016-12-24 2 views
5

Я занимаюсь анализом terminfo файлов базы данных, которые являются типом двоичных файлов. Вы можете прочитать о своем storage format своими силами и подтвердить проблему, с которой я сталкиваюсь.read little endian 16-разрядное целое без знака

Руководство говорит -

Секция заголовка начинается файл. Этот раздел содержит шесть коротких целых чисел в формате, описанном ниже. Эти целые числа:

(1) магическое число (октал 0432);

...

...

Короткие целые числа хранятся в двух 8-битовых байтов. Первый байт содержит наименее значимые 8 бит значения, , а второй байт содержит наиболее важные 8 бит. (Таким образом, представленное значение составляет 256 * второй + первый.) Значение -1 представлено двумя байтами 0377, 0377; другие отрицательные значения являются незаконными. Это значение обычно означает , что соответствующая возможность отсутствует на этом терминале . Машины, в которых это не соответствует аппарату , должны считывать целые числа в виде двух байтов и вычислять значение little-endian.


  • Первая проблема при анализе этого типа ввода является то, что он фиксирует размер 8 бит, так что обычный старый символ не может использоваться, поскольку он не гарантирует размер, чтобы быть точно 8 бит. Таким образом, я смотрел «Fixed width integer types», но снова столкнулся с выбором из b/w int8_t или uint8_t, который четко указывает - «предоставляется, только если реализация напрямую поддерживает тип». Итак, что я должен выбрать, чтобы тип был достаточно переносимым.

  • Вторая проблема заключается в отсутствии метода buffer.readInt16LE() в стандартной библиотеке C++, которая может читать 16 байтов данных в формате Little Endian. Итак, как я должен продолжить реализацию этой функции снова в переносном & безопасном пути.

Я уже пробовал читать его с char типа данных, но это, безусловно, производит мусор на моей машине. Правильный ввод может быть прочитан командой infocmp, например, - $ infocmp xterm.


#include <fstream> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    std::ifstream db(
     "/usr/share/terminfo/g/gnome", std::ios::binary | std::ios::ate); 

    std::vector<unsigned char> buffer; 

    if (db) { 
     auto size = db.tellg(); 
     buffer.resize(size); 
     db.seekg(0, std::ios::beg); 
     db.read(reinterpret_cast<char*>(&buffer.front()), size); 
    } 
    std::cout << "\n"; 
} 

$1 = std::vector of length 3069, capacity 3069 = {26 '\032', 1 '\001', 21 '\025', 
    0 '\000', 38 '&', 0 '\000', 16 '\020', 0 '\000', 157 '\235', 1 '\001', 
    193 '\301', 4 '\004', 103 'g', 110 'n', 111 'o', 109 'm', 101 'e', 124 '|', 
    71 'G', 78 'N', 79 'O', 77 'M', 69 'E', 32 ' ', 84 'T', 101 'e', 114 'r', 
    109 'm', 105 'i', 110 'n', 97 'a', 108 'l', 0 '\000', 0 '\000', 1 '\001', 
    0 '\000', 0 '\000', 1 '\001', 0 '\000', 0 '\000', 0 '\000', 0 '\000', 
    0 '\000', 0 '\000', 0 '\000', 0 '\000', 1 '\001', 1 '\001', 0 '\000', 
.... 
.... 
+1

Опубликовать код какой-либо другой. попробуйте прочитать несколько байтов в буфер и взглянуть на него с помощью вашего отладчика. –

+1

'char' гарантированно будет наименьшим адресуемым устройством в любой системе (поэтому' sizeof (char) 'указано всегда как' 1'). Таким образом, для системы с 8-битными байтами 'char' гарантированно будет 8 бит. И поскольку это практически * все * системы, созданные за последние 30 лет или около того, нет необходимости беспокоиться. Если вам нужно перенести свою программу на какую-то старую систему (или более старую) в 1970-е годы, тогда вам может понадобиться беспокоиться об этом, но не иначе. –

+1

Вы должны использовать 'uint8_t' или' unsigned char'. Тип 'char' может быть' unsigned', 'signed' или' char' * в зависимости от настройки компилятора *. –

ответ

2

Первая проблема при анализе этого типа ввода является то, что он фиксирует размер 8 бит, так что обычный старый символ не может быть использован, так как он не гарантирует, что размер будет ровно 8 бит.

Любое целое число, состоящее как минимум из 8 бит, в порядке.В то время как char не гарантированно составляет ровно 8 бит, требуется, по меньшей мере, 8 бит, так как размер зависит от размера, нет никаких проблем, кроме как вы можете в некоторых случаях замаскировать старшие биты, если они существуют , Однако char может не быть без знака, и вы не хотите, чтобы октеты интерпретировались как знаковые значения, поэтому вместо этого используйте unsigned char.

Вторая проблема заключается в отсутствии метода buffer.readInt16LE() в стандартной библиотеке C++, который мог бы читать 16 байтов данных в формате Little Endian. Итак, как я должен продолжить реализацию этой функции снова в переносном & безопасном пути.

Прочитайте один октет за раз в unsigned char. Назначьте первый октет переменной (достаточно большой для представления не менее 16 бит). Сдвиньте биты второго октета, оставшегося на 8, и назначьте переменную с помощью соединения поразрядным или.

Или еще лучше, не переустанавливайте его, а используйте стороннюю существующую библиотеку.

Я уже пробовал читать его с типом данных char, но он определенно производит мусор на моей машине.

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

+0

Я добавил код и образец вывода, не могли бы вы сказать мне, что я делаю с ним неправильно. –

+0

hmm, поэтому я попытался определить два 'unit8_t' vars' x' & 'y' и прочитать данные, используя -' db.read (reinterpret_cast (& x), sizeof (x)); 'и затем сделал то, что вы предложили в другом 'unit16_t' -' result = x | (y << 8); 'и результат был' 282', который действительно '0432' в восьмеричном. Не так уверен, почему вывод отладчика был таким. Так работает ли этот метод как на маленькой, так и на большой конечной машине? –

+0

Да, это преобразует малое значение endian в native endian, независимо от того, что такое внутренняя энтианность. – user2079303

Смежные вопросы