В чем разница между 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 не делает никаких проверок.

726   6  

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 возвращается для "input failure", что означает, что ssacnf() никогда не вернется EOF.

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

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

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

To @R.. Я думаю, что это не достаточно, чтобы проверить 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(), ни 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 на введенной строке итеративно

Comments

    Ничего не найдено.