Как быстро и безопасно преобразовать шестнадцатеричную строку в 64-разрядное целое число без знака (uint64_t)? - PullRequest
7 голосов
/ 09 ноября 2010

Я пытался

sscanf(str, "%016llX", &int64 );

, но кажется небезопасным.Есть ли быстрый и безопасный способ выполнить приведение типа?

Спасибо ~

Ответы [ 3 ]

8 голосов
/ 09 ноября 2010

Не беспокойтесь о функциях семейства scanf. Их практически невозможно использовать надежно. Вот общее безопасное использование strtoull:

char *str, *end;
unsigned long long result;
errno = 0;
result = strtoull(str, &end, 16);
if (result == 0 && end == str) {
    /* str was not a number */
} else if (result == ULLONG_MAX && errno) {
    /* the value of str does not fit in unsigned long long */
} else if (*end) {
    /* str began with a number but has junk left over at the end */
}

Обратите внимание, что strtoull принимает необязательный префикс 0x для строки, а также необязательный начальный пробел и символ знака (+ или -). Если вы хотите отклонить их, вы должны выполнить тест перед вызовом strtoull, например:

if (!isxdigit(str[0]) || (str[1] && !isxdigit(str[1])))

Если вы также хотите запретить чрезмерно длинные представления чисел (начальные нули), вы можете проверить следующее условие перед вызовом strtoull:

if (str[0]=='0' && str[1])

Еще одна вещь, которую нужно иметь в виду, это то, что «отрицательные числа» не рассматриваются вне диапазона конверсии; вместо этого префикс - обрабатывается так же, как и унарный оператор отрицания в C, применяемый к значению без знака, поэтому, например, strtoull("-2", 0, 16) вернет ULLONG_MAX-1 (без установки errno).

2 голосов
/ 09 ноября 2010

Ваш заголовок (в настоящее время) противоречит предоставленному вами коду.Если вы хотите сделать то, чем изначально был ваш заголовок (преобразовать строку в целое число), то вы можете использовать этот ответ.


Вы можете использовать функцию strtoull,которая в отличие от sscanf является функцией, специально предназначенной для чтения текстовых представлений чисел.

const char *test = "123456789abcdef0";

errno = 0;
unsigned long long result = strtoull(test, NULL, 16);

if (errno == EINVAL)
{
    // not a valid number
}
else if (errno == ERANGE)
{
    // does not fit in an unsigned long long
}
0 голосов
/ 09 ноября 2010

В то время, когда я писал этот ответ, ваш заголовок предполагал, что вы захотите записать uint64_t в строку, тогда как ваш код сделал обратное (чтение шестнадцатеричной строки в uint64_t).Я ответил «в обе стороны»:

В заголовке <inttypes.h> есть макросы преобразования для безопасной обработки типов ..._t:

#include <stdio.h>
#include <inttypes.h>

sprintf( str, "%016" PRIx64, uint64 );

Или (если это действительно то, что вы пытаетесьсделать), наоборот:

#include <stdio.h>
#include <inttypes.h>

sscanf( str, "%" SCNx64, &uint64 );

Обратите внимание, что вы не можете применять ширину и т. д. с семейством функций scanf().Он анализирует то, что получает, что может дать нежелательные результаты, когда ввод не соответствует ожидаемому форматированию.Да, и семейство функций scanf() знает только (строчные буквы) "x", но не (прописные) "X".

...