web-dev-qa-db-fra.com

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

gcc 4.4.4 c89

Quoi de mieux pour convertir une chaîne 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 device\n");
    return FALSE;
}

ou en utilisant atoi

device_num = atoi(digits);

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

63
ant2009

Vous avez 3 choix:

  1. atoi

C'est probablement le plus rapide si vous l'utilisez dans un code critique en termes de performances, mais cela ne génère pas de rapport d'erreur. Si la chaîne ne commence pas par un entier, elle retournera 0. Si la chaîne contient des indésirables après l'entier, elle convertira la partie initiale et ignorera le reste. Si le nombre est trop grand pour tenir dans int, le comportement n'est pas spécifié.

  1. sscanf

Certains rapports d’erreur, et vous avez beaucoup de flexibilité pour ce type de stockage (versions signée/non signée de char/short/int/long/long long/size_t/ptrdiff_t/intmax_t).

La valeur renvoyée correspond au nombre de conversions réussies. Par conséquent, recherchez "%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 l'entier lu dans une autre variable et vérifier de ce fait si la chaîne entière a été convertie ou s'il y a des erreurs après. Cependant, comme atoi, le comportement en cas de dépassement d'entier n'est pas spécifié.

  1. strtol et famille

Rapport d'erreurs robuste, à condition que vous ayez défini errno sur 0 avant de passer l'appel. Les valeurs de retour sont spécifiées lors du débordement et errno sera défini. Vous pouvez choisir n’importe quelle base de chiffres comprise entre 2 et 36, ou spécifier 0 comme base pour l’interprétation automatique en début de chaîne 0x et 0 comme hex et octal, respectivement. Les choix du type de conversion sont les versions signée/non signée de long/long long/intmax_t.

Si vous avez besoin d’un type plus petit, vous pouvez toujours stocker le résultat dans un fichier temporaire long ou unsigned long variable et vérifiez vous-même le débordement.

Etant donné que ces fonctions prennent un pointeur à un argument de pointeur, vous obtenez également un pointeur sur le premier caractère suivant l’entier converti, gratuitement. Vous pouvez ainsi déterminer si la chaîne entière est un entier ou analyser les données suivantes dans la chaîne si nécessaire.


Personnellement, je recommanderais la famille strtol à des fins la plupart. Si vous faites quelque chose de rapide et sale, atoi peut répondre à vos besoins.

En passant, je trouve parfois nécessaire d'analyser les nombres lorsque les espaces, les signes, etc. ne sont pas censés être acceptés. Dans ce cas, il est très facile de lancer sa propre 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 */ 
106
R..

*scanf() la famille de fonctions renvoie le nombre de valeurs converties. Donc, vous devriez vérifier que sscanf() renvoie 1 dans votre cas. EOF est renvoyé pour "échec d'entrée", ce qui signifie que ssacnf() ne retournera jamais EOF.

Pour sscanf(), la fonction doit analyser la chaîne de formatage, puis décoder un entier. atoi() n'a pas cette surcharge. Tous deux souffrent du problème que les valeurs hors limites entraînent un comportement indéfini.

Vous devez utiliser strtol() ou strtoul(), fonctions qui fournissent une détection et une vérification des erreurs bien meilleures. Ils vous ont également fait savoir si toute la chaîne avait été consommée.

Si vous voulez un int, vous pouvez toujours utiliser strtol(), puis vérifiez la valeur renvoyée pour voir si elle est comprise entre INT_MIN et INT_MAX.

10
Alok Singhal

Pour @R .. Je pense qu'il n'est pas suffisant de vérifier errno pour la détection d'erreur dans strtol call.

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

Vous devrez également vérifier EndPointer pour rechercher des erreurs.

4
PickBoy

Combiner R .. et PickBoy répond par souci de concision

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

// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
2
Steven Penny

Lorsqu'il n'y a aucune inquiétude concernant l'entrée de chaîne non valide ou des problèmes de plage, 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 tout prêt détaille le manque de vérification d'erreur avec atoi() et une erreur de vérification avec sscanf().

strtol() est la fonction la plus stricte pour convertir une chaîne en int. Ce n'est pourtant qu'un début. Vous trouverez ci-dessous des exemples détaillés montrant l’utilisation correcte et la raison de cette réponse après le accepté .

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

Ceci est similaire à atoi() et néglige d'utiliser les fonctions de détection d'erreur de strtol().

Pour utiliser pleinement strtol(), vous devez prendre en compte plusieurs fonctionnalités:

  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: Est-ce que "123xyz" Est OK?

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '\0') return FAIL_EXTRA_JUNK;
    
  3. Détecter si la valeur était si grande, le résultat ne peut pas être représenté sous forme de long comme "999999999999999999999999999999".

    errno = 0;
    long L = strtol(nptr, &endptr, 10);
    if (errno == ERANGE) return FAIL_OVERFLOW;
    
  4. Détecter si la valeur était en dehors de la plage que int, mais pas long. Si int et long ont la même plage, ce test 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 définissent errno pour des raisons supplémentaires telles que errno renvoie EINVAL si aucune conversion n'a été effectuée ou EINVAL. La valeur de le paramètre Base n'est pas valide. . Le meilleur moment pour tester ces valeurs errno dépend de la mise en œuvre.

Rassembler tout cela: (adaptez-vous à 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 != '\0') {
    *error_code = FAIL_EXTRA_JUNK;
  } else if (errno) {
    *error_code = FAIL_IMPLEMENTATION_REASON;
  }
  return (int) i;
}

Remarque: Toutes les fonctions mentionnées autorisent les espaces et les espaces de début, un caractère facultatif et le signe , et sont affectées par locale changer. Un code supplémentaire est requis pour une conversion plus restrictive.


Remarque: le changement de titre non-OP est faussé. Cette réponse s’applique mieux au titre original "convertir une chaîne en un entier sscanf ou atoi"

2
chux

Si l'utilisateur entre 34abc et que vous le transmettez à atoi, il retournera 34. Si vous souhaitez valider la valeur entrée, vous devez utiliser isdigit sur la chaîne entrée de manière itérative.

0
Raghuram