преобразование строки, прочитанной из двоичного файла, в целое число

У меня есть бинарный файл. я читаю 16 байт за раз, используя fstream.

Я хочу преобразовать его в целое число. Я попробовал атои. но это не сработало. В python мы можем сделать это, преобразовав поток байтов, используя stringobtained.encode('utf-8'), а затем преобразовав его в int, используя int(bytestring.hex(),16). Должны ли мы следовать таким сложным шагам, как в python, или есть способ преобразовать его напрямую?

ifstream file(binfile, ios::in | ios::binary | ios::ate);
if (file.is_open())
{

    size = file.tellg();
    memblock = new char[size];
    file.seekg(0, ios::beg);
    while (!file.eof())
    {
        file.read(memblock, 16);            
        int a = atoi(memblock); // doesnt work 0 always
        cout << a << "\n";
        memset(memblock, 0, sizeof(memblock));
    }
    file.close();

Редактировать:

Это образец содержимого файла.

53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00
04 00 01 01 00 40 20 20 00 00 05 A3 00 00 00 47
00 00 00 2E 00 00 00 3B 00 00 00 04 00 00 00 01

Мне нужно прочитать его как 16 байт, т.е. 32 шестнадцатеричных цифры за раз (т.е. одну строку в содержимом файла образца) и преобразовать его в целое число. поэтому при чтении 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 я должен получить 110748049513798795666017677735771517696

Но я не мог этого сделать. Я всегда получаю 0 даже после попытки strtoull. Я неправильно читаю файл или что я упускаю.


person user00011    schedule 07.01.2019    source источник
comment
atoi() преобразует текстовую строку, заканчивающуюся нулем, в целое число. Если только в ваших 16 байтах последний байт не установлен на '\0', а предыдущие пятнадцать байтов состоят из начальных пробелов, необязательного знака минус и по крайней мере еще одного символа между «0» и «9», тогда atoi() не будет работать, потому что это то, что он делает, и единственное, что он делает (технически '\0' не обязательно должен быть последним байтом, но это не важная деталь).   -  person Sam Varshavchik    schedule 07.01.2019
comment
int обычно составляет 4 байта, которые не могут хранить 16-байтовое значение.   -  person NathanOliver    schedule 07.01.2019
comment
Нравится? stackoverflow.com/q/1070497   -  person Robert Harvey    schedule 07.01.2019
comment
@NathanOliver: предположительно, это 16 шестнадцатеричных цифр. Восьмибайтовый int сделает это.   -  person Robert Harvey    schedule 07.01.2019
comment
Итак, это шестнадцать шестнадцатеричных кусочков. Скопируйте 16 байтов в 17-байтовый массив char, добавьте к нему '\0' в конце и передайте его strtoull(). Конец.   -  person Sam Varshavchik    schedule 07.01.2019
comment
size = file.tellg(); Я считаю, что это должно вернуть 0, так как файл был только что открыт. Кроме того, sizeof(memblock) вернет размер указателя, а не длину буфера.   -  person Johnny Mopp    schedule 07.01.2019
comment
Какая связь между байтами в файле и целочисленным значением? То есть, что должен сделать код, чтобы преобразовать байты в целочисленное значение?   -  person Pete Becker    schedule 07.01.2019
comment
Пожалуйста, покажите фактические данные файла и как должно выглядеть результирующее целое число   -  person Remy Lebeau    schedule 07.01.2019
comment
@JohnnyMopp Я думал то же самое, пока не посмотрел, что делает ios::ate. Эта часть кода в порядке.   -  person Mark Ransom    schedule 08.01.2019
comment
@RobertHarvey, если вы посмотрите на код Python, он действительно преобразуется в 16-байтовое целое число, хотя и неудобным образом: .hex() преобразует 16-байтовую строку в 32 шестнадцатеричных цифры. Целые числа Python не ограничены; нет подходящего типа C++ для этой проблемы.   -  person Mark Ransom    schedule 08.01.2019


Ответы (3)


У вас тут ряд проблем. Во-первых, C++ не имеет стандартного 128-битного целочисленного типа. Возможно, вы сможете найти расширение компилятора, см., например, Есть ли 128-битное целое число в gcc? или Есть ли 128-битное целое число в C++?.

Во-вторых, вы пытаетесь декодировать необработанные байты вместо строки символов. atoi остановится на первом нецифровом символе, с которым он столкнется, который 246 раз из 256 будет самым первым байтом, поэтому он возвращает ноль. Если вам очень не повезло, вы прочитаете 16 допустимых цифр, а atoi начнет читать неинициализированную память, что приведет к неопределенному поведению.

Вам все равно не нужно atoi, ваша проблема намного проще. Вам просто нужно собрать 16 байтов в целое число, что можно сделать с помощью операций сдвига и or. Единственная сложность заключается в том, что read нужен тип char, который, вероятно, будет знаковым, а вам нужны байты без знака.

ifstream file(binfile, ios::in | ios::binary);
char memblock[16];
while (file.read(memblock, 16))
{
    uint128_t a = 0;
    for (int i = 0; i < 16; ++i)
    {
        a = (a << 8) | (static_cast<unsigned int>(memblock[i]) & 0xff);
    }
    cout << a << "\n";
}
file.close();
person Mark Ransom    schedule 08.01.2019
comment
Спасибо за идею. Но ни uint128_t, ни unsigned __int128 у меня не работали. Поскольку я использую компилятор MS VSC++. Но я использовал ваш способ и читал по 4 байта за раз и сохранял в строке битовый набор, используемый для объединения этих строк для формирования 128-битного двоичного эквивалента 16-байтового шестнадцатеричного значения. На данный момент я использую это значение и сдвиг вправо, поэтому мне не нужно сохранять результирующую 128-битную двоичную строку. Но есть идеи, как использовать беззнаковый __128 в vsc++? Это сэкономило бы много времени - person user00011; 09.01.2019
comment
@Prakrithi, согласно этому, в настоящее время нет возможности использовать VSC++. Я только что просмотрел библиотеки в VS2017 и могу подтвердить. - person Mark Ransom; 09.01.2019

16 шестнадцатеричных цифр означают, что результат может иметь длину 8 байт. Поэтому atoi не работает. Мы не можем дать radix atoi, и он возвращает int, который обычно представляет собой 4-байтовое целое число. Возможно, мы можем использовать std::stoull.

Пример:

#include <iostream> 
#include <string>

int main()
{ 
    std::string s = "0000abaafffefffe";
    unsigned long long x = std::stoull(s, nullptr, 16);
    std::cout << x << "\n"; 
} 

188750927691774

person Öö Tiib    schedule 07.01.2019
comment
Это был бы идеальный ответ, если бы он также включал бит о чтении строки из файла. - person Mark Ransom; 07.01.2019
comment
Это звучит хорошо. Но я хочу прочитать 32 шестнадцатеричных цифры. И еще один вопрос: указанная вами шестнадцатеричная строка имеет тот же формат, который я получаю в буфере, который я читаю? - person user00011; 07.01.2019
comment
@Prakrithi 16-байтовые целые числа необычны и только иногда доступны в качестве расширения компилятора, поэтому укажите платформу в своем вопросе и опишите в ней формат вашего файла побайтно (люди только догадываются об этом в комментариях). С примерами, какие байты находятся в файле и какие числа вы хотите. - person Öö Tiib; 08.01.2019
comment
Вы забыли упомянуть платформу и компилятор - person Öö Tiib; 08.01.2019

Это число двоичное, что вы хотите:

    short value ;
    file.read(&value, sizeof (value));            

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

person user3344003    schedule 07.01.2019