В чем разница между sscanf или atoi для преобразования строки в целое число? - PullRequest
61 голосов
/ 06 августа 2010

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 device\n");
    return FALSE;
}

или используя atoi

device_num = atoi(digits);

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

Ответы [ 6 ]

104 голосов
/ 06 августа 2010

У вас есть 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 как шестнадцатеричные и восьмеричные соответственно. Выбор типа для преобразования является подписанной / неподписанной версией 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 */ 
10 голосов
/ 06 августа 2010

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

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

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

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

4 голосов
/ 15 марта 2012

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

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

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

2 голосов
/ 14 декабря 2015

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

В противном случае метод с лучшим обнаружением ошибки / диапазона не является ни atoi(), ни 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. Следует ли преобразовать всю строку или только ведущую часть: "123xyz" ОК?

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '\0') 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 Значение параметра Base недопустимо. .Лучшее время для проверки этих 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 != '\0') {
    *error_code = FAIL_EXTRA_JUNK;
  } else if (errno) {
    *error_code = FAIL_IMPLEMENTATION_REASON;
  }
  return (int) i;
}

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


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

2 голосов
/ 10 апреля 2012

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

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

// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
0 голосов
/ 06 августа 2010

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

...