Quelle est la différence entre sscanf ou atoi pour convertir une chaîne en un entier?

gcc 4.4.4 c89

ce qui est mieux pour convertir une chaîne de caractères en une valeur entière.

j'ai essayé 2 méthodes différentes atoi et sscanf. Les deux fonctionnent comme prévu.

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

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

ou en utilisant atoi

device_num = atoi(digits);

je pensais que le sscanf serait mieux que vous pouvez vérifier les erreurs. Cependant, atoi ne fait aucune vérification.

56

6 réponses

vous avez 3 choix:

  1. atoi

c'est probablement le plus rapide si vous l'utilisez dans le code de performance critique, mais il ne fait pas de rapport d'erreur. Si la chaîne ne commence pas par un entier, elle retournera 0. Si la chaîne contient de la camelote après le nombre entier, elle convertira la partie initiale et ignorera le reste. Si le nombre est trop grand pour tenir dans int , le comportement est indéterminé.

  1. sscanf

certains rapports d'erreur, et vous avez beaucoup de flexibilité pour quel type de stocker (signé/non signé des versions de char/short/int/long/long long/size_t/ptrdiff_t/intmax_t ).

la valeur de retour est le nombre de conversions qui réussissent, donc la recherche de "%d" retournera 0 si la chaîne ne commence pas par un entier. Vous pouvez utiliser "%d%n" pour stocker l'index du premier caractère après le nombre entier qui est lisez dans une autre variable, et vérifiez ainsi si la chaîne entière a été convertie ou s'il y a de la camelote par la suite. Cependant, comme atoi , le comportement sur le débordement entier est non spécifié.

  1. strtol et la famille
"1519180920 Robuste" erreur de déclaration, à condition de définir errno à 0 avant de faire l'appel. Les valeurs de retour sont spécifiées sur overflow et errno sera défini. Vous pouvez choisir n'importe quel nombre de base à partir de 2 à 36, ou spécifiez 0 comme base pour auto-interpréter le début 0x et 0 comme hex et octal, respectivement. Les choix de type pour convertir en sont signés/non signés des versions de long/long long/intmax_t .

si vous avez besoin d'un type plus petit, vous pouvez toujours stocker le résultat dans une variable temporaire long ou unsigned long et vérifier vous-même le débordement.

puisque ces fonctions prennent un argument pointeur à Pointeur, vous obtenez également un pointeur à la premier caractère suivant le nombre entier converti, gratuitement, de sorte que vous pouvez dire si la chaîne entière était un nombre entier ou analyser les données suivantes dans la chaîne si nécessaire.


personnellement, je recommande la strtol famille pour la plupart buts. Si vous faites quelque chose de rapide et sale, atoi pourrait répondre à vos besoins.

comme une mise à part, parfois je trouve que je dois analyser les nombres où conduire espace blanc, signe, etc. ne sont pas censés être acceptés. Dans ce cas, il est assez sacrément facile de rouler votre propre pour boucle, par exemple.,

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

ou vous pouvez utiliser (pour la robustesse):

if (isdigit(*s))
    x=strtol(s, &s, 10);
else /* error */ 
99
répondu R.. 2016-12-10 06:02:05

*scanf() famille de fonctions retourner le nombre de valeurs converties. Vous devriez donc vérifier pour vous assurer que sscanf() retourne 1 dans votre cas. EOF est retourné pour" input failure", ce qui signifie que ssacnf() ne retournera jamais EOF .

pour sscanf() , la fonction doit analyser la chaîne de format, puis décoder un entier. atoi() n'a pas ces frais généraux. Tous les deux souffrent du problème que les valeurs hors de portée résultent dans un comportement indéfini.

vous devez utiliser les fonctions strtol() ou strtoul() , qui offrent une meilleure détection des erreurs et un meilleur contrôle. Ils vous font aussi savoir si la corde entière a été consommée.

Si vous voulez un int , vous pouvez toujours utiliser strtol() , puis vérifier la valeur retournée pour voir si elle se situe entre INT_MIN et INT_MAX .

10
répondu Alok Singhal 2010-08-06 02:37:33

To @R.. Je pense qu'il ne suffit pas de vérifier errno pour la détection d'erreur dans l'appel strtol .

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

vous devrez également vérifier EndPointer pour les erreurs.

4
répondu PickBoy 2012-03-15 06:46:29

Combinant R.. et PickBoy répond pour la brièveté

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

// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
2
répondu Steven Penny 2012-04-10 06:57:14

Lorsqu'il n'y a pas de préoccupation concernant les entrées de chaîne ou les problèmes de portée invalides, utilisez le plus simple: atoi()

sinon , la méthode avec la meilleure détection d'erreur/plage n'est ni atoi() , ni sscanf() . Cette bonne réponse tous les détails prêts à l'absence de vérification d'erreur avec atoi() et certains vérification d'erreur avec sscanf() .

strtol() est le plus strict fonction de conversion d'une chaîne en int . Pourtant, il n'est qu'un début. Ci-dessous sont des exemples détaillés pour montrer l'usage approprié et donc la raison de cette réponse après le accepté un .

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

C'est comme atoi() et néglige d'utiliser l'erreur de détection de caractéristiques de strtol() .

pour utiliser pleinement strtol() , il y a plusieurs caractéristiques à considérer:

  1. détection de pas de conversion : exemples: "xyz" , ou "" ou "--0" ? Dans ces cas, endptr correspondra à nptr .

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (nptr == endptr) return FAIL_NO_CONVERT;
    
  2. la chaîne entière doit-elle être convertie ou seulement la partie principale: "123xyz" est-elle OK?

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '"151920920"') return FAIL_EXTRA_JUNK;
    
  3. Détecter si la valeur était si grand, le résultat n'est pas représentable comme un long comme "999999999999999999999999999999" .

    errno = 0;
    long L = strtol(nptr, &endptr, 10);
    if (errno == ERANGE) return FAIL_OVERFLOW;
    
  4. Détecter si la valeur est en dehors de la plage de int , mais pas long . Si int et long ont la même portée, cet essai n'est pas nécessaire.

    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    
  5. certaines implémentations vont au-delà de la norme C et mettent errno pour des raisons supplémentaires telles que errno à EINVAL dans le cas où aucune conversion n'a été exécuté ou EINVAL la valeur du paramètre de Base n'est pas valide. . Le meilleur moment pour tester ces valeurs errno dépend de l'implémentation.

Mettre tous ensemble: (à Adapter à vos besoins)

#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 != '"151950920"') {
    *error_code = FAIL_EXTRA_JUNK;
  } else if (errno) {
    *error_code = FAIL_IMPLEMENTATION_REASON;
  }
  return (int) i;
}

Note: toutes les fonctions mentionnées permettent des espaces de tête, un caractère facultatif de tête signal et sont touchés par le changement locale . Un code supplémentaire est requis pour une conversion plus restrictive.


Note: changement de titre non-OP accentuation asymétrique. Cette réponse s'applique mieux à l'original du titre "convertit une chaîne en nombre entier sscanf ou atoi"

2
répondu chux 2017-05-23 12:25:53

si l'utilisateur entre 34abc et que vous le passez à atoi il retournera 34. Si vous voulez valider la valeur entrée alors vous devez utiliser isdigit sur la chaîne entrée itérativement

0
répondu Raghuram 2010-08-06 02:43:25