В чем разница между sscanf или atoi для преобразования строки в целое число?

gcc 4.4.4 c89

что лучше преобразовать строку в целое значение.

Я пробовал 2 разных метода atoi и sscanf. Оба работают, как и ожидалось.

char digits[3] = "34";
int device_num = 0;

if(sscanf(digits, "%d", &device_num) == EOF) {
    fprintf(stderr, "WARNING: Incorrect value for devicen");
    return FALSE;
}

или использование atoi

device_num = atoi(digits);

Я думал, что sscanf будет лучше, поскольку вы можете проверить наличие ошибок. Однако atoi не делает никакой проверки.

6 ответов


у вас есть 3 варианта:

  1. atoi

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

  1. sscanf

некоторые отчеты об ошибках, и у вас есть много гибкости для того, какой тип хранить (подписанные/неподписанные версии char/short/int/long/long long/size_t/ptrdiff_t/intmax_t).

возвращаемое значение-это количество успешных преобразований, поэтому сканирование для "%d" возвращает 0, если строка не начинается с числа. Вы можете использовать "%d%n" чтобы сохранить индекс первого символа после целого числа, которое читается в другой переменной, и тем самым проверить посмотрите, была ли вся строка преобразована или есть мусор после этого. Однако, как atoi, поведение при переполнении целого числа не указано.

  1. strtol и семьей

надежная отчетность об ошибках, при условии, что вы установили errno до 0 перед вызовом. Возвращаемые значения указываются при переполнении и errno будет установлен. Вы можете выбрать любую счисления от 2 до 36, или указать 0 в качестве базы для автоматической интерпретации ведущих 0x и 0 как hex и восьмеричный, соответственно. Выбор типа для преобразования-это подписанные / неподписанные версии long/long long/intmax_t.

Если вам нужен меньший тип, вы всегда можете сохранить результат во временном long или unsigned long переменная и проверьте переполнение самостоятельно.

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


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

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

for (x=0; (unsigned)*s-'0'<10; s++) 
    x=10*x+(*s-'0');

или вы можете использовать (для робастность):

if (isdigit(*s))
    x=strtol(s, &s, 10);
else /* error */ 

*scanf() семейство функций возвращает количество преобразованных значений. Поэтому вы должны проверить, чтобы убедиться sscanf() возвращает 1 в вашем случае. EOF возвращается для "сбоя ввода", что означает, что ssacnf() никогда не вернется EOF.

на sscanf(), функция должна проанализировать строку формата, а затем декодировать целое число. atoi() нет, что накладные. Оба страдают от проблемы, что значения вне диапазона приводят к неопределенному поведению.

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

если вы хотите int, вы всегда можете использовать strtol(), а затем проверьте возвращаемое значение, чтобы увидеть, находится ли оно между INT_MIN и INT_MAX.


К @Р.. Я думаю, что недостаточно проверить errno для обнаружения ошибок в strtol звонок.

long strtol (const char *String, char **EndPointer, int Base)

Вам также нужно проверить EndPointer для ошибок.


Объединение Р.. и PickBoy отвечает для краткости

long strtol (const char *String, char **EndPointer, int Base)

// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);

когда нет никаких проблем с недопустимым вводом строки или диапазоном, используйте самый простой:atoi()

в противном случае метод с лучшим обнаружением ошибок / диапазона не является ни atoi(), nor sscanf(). это хороший ответ все готовые детали отсутствие ошибок с atoi() и некоторые проверка ошибок с помощью sscanf().

strtol() наиболее строгие функция преобразования строки int. Но это только начало. Ниже подробные примеры, чтобы показать правильное использование и поэтому причину этого ответа после принят.

// Over-simplified use
int strtoi(const char *nptr) {
  int i = (int) strtol(nptr, (char **)NULL, 10);
  return i; 
}

это как atoi() и не использовать функции обнаружения ошибок strtol().

полностью использовать strtol(), есть различные функции, чтобы рассмотреть:

  1. определение преобразование примеры: "xyz" или "" или "--0"? В этих случаях endptr будет соответствовать nptr.

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (nptr == endptr) return FAIL_NO_CONVERT;
    
  2. должна ли вся строка преобразовываться или только ведущая часть: Is "123xyz" ОК?

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '') return FAIL_EXTRA_JUNK;
    
  3. обнаружить, если значение было настолько большим, результат не представляется как long как "999999999999999999999999999999".

    errno = 0;
    long L = strtol(nptr, &endptr, 10);
    if (errno == ERANGE) return FAIL_OVERFLOW;
    
  4. определить, если значение было за пределами диапазона, чем int, но не long. Если int и long имейте такой же ряд, этот тест нет необходимый.

    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    
  5. некоторые реализации выходят за рамки стандарта C и устанавливают errno по дополнительным причинам, таким как errno в EINVAL в случае, если преобразование не было выполнено или EINVAL значение базового параметра недопустимо.. Лучшее время для тестирования этих errno значения зависят от реализации.

положить все это вместе: (отрегулируйте потребности)

#include <errno.h>
#include <stdlib.h>

int strtoi(const char *nptr, int *error_code) {
  char *endptr;
  errno = 0;
  long i = strtol(nptr, &endptr, 10);

  #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (errno == ERANGE || i > INT_MAX || i < INT_MIN) {
    errno = ERANGE;
    i = i > 0 : INT_MAX : INT_MIN;
    *error_code = FAIL_INT_OVERFLOW;
  }
  #else
  if (errno == ERANGE) {
    *error_code = FAIL_OVERFLOW;
  }
  #endif

  else if (endptr == nptr) {
    *error_code = FAIL_NO_CONVERT;
  } else if (*endptr != '') {
    *error_code = FAIL_EXTRA_JUNK;
  } else if (errno) {
    *error_code = FAIL_IMPLEMENTATION_REASON;
  }
  return (int) i;
}

Примечание: все упомянутые функции позволяют ведущим космосам, опционному ведущему знак характер и влияют на locale изменить. Для более строгого преобразования требуется дополнительный код.


Примечание: не-OP изменение заголовка перекос акцент. Этот ответ лучше применим к исходному заголовку "преобразовать строку в целое число sscanf или atoi"


Если пользователь вводит 34abc, и вы передаете их atoi, он вернет 34. Если вы хотите проверить введенное значение, вам нужно использовать isdigit в введенной строке итеративно