Подтвердить что ты не робот

В чем разница между 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 device\n");
    return FALSE;
}

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

device_num = atoi(digits);

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

4b9b3361

Ответ 1

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

  • 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 (x=0; (unsigned)*s-'0'<10; s++) 
    x=10*x+(*s-'0');

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

Если нет проблем с неправильными вводами строк или диапазоном, используйте самые простые: 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(), можно рассмотреть различные функции:

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

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

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

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

    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    
  • Некоторые реализации выходят за рамки стандарта 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;
}

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


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

Ответ 6

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