Où est la fonction itoa sous Linux?


139

itoa()est une fonction très pratique pour convertir un nombre en chaîne. Linux ne semble pas avoir itoa(), y a-t-il une fonction équivalente ou dois-je utiliser sprintf(str, "%d", num)?


4
une raison de ne pas utiliser sprintf(str, "%d", num)? est-ce beaucoup plus lent que itoa?
javapowered le

1
@javapowered, pour sa part, itoaautorise la conversion de base arbitraire, printfcontrairement aux spécificateurs.
vladr

@javapowered sprintf () n'est pas sûr pour le signal
lunesco

Une raison de ne pas utiliser gcvt()de bibliothèque standard?
Subin Sebastian

Réponses:


100

EDIT: Désolé, j'aurais dû me rappeler que cette machine est décidément non standard, après avoir branché diverses libcimplémentations non standard à des fins académiques ;-)

Comme il itoa()est en effet non standard, comme mentionné par plusieurs commentateurs utiles, il est préférable d'utiliser sprintf(target_string,"%d",source_int)ou (mieux encore, car il est à l'abri des débordements de tampon) snprintf(target_string, size_of_target_string_in_bytes, "%d", source_int). Je sais que ce n'est pas aussi concis ou cool que itoa(), mais au moins vous pouvez écrire une fois, exécuter partout (tm) ;-)

Voici l'ancienne réponse (modifiée)

Vous avez raison de dire que la valeur par défaut gcc libcn'inclut pas itoa(), comme plusieurs autres plates-formes, car elle ne fait pas techniquement partie de la norme. Voir ici pour un peu plus d'informations. Notez que vous devez

#include <stdlib.h>

Bien sûr, vous le savez déjà, car vous vouliez l' utiliser itoa() sous Linux après l'avoir probablement utilisé sur une autre plate-forme, mais ... le code (volé à partir du lien ci-dessus) ressemblerait à:

Exemple

/* itoa example */
#include <stdio.h>
#include <stdlib.h>

int main ()
{
  int i;
  char buffer [33];
  printf ("Enter a number: ");
  scanf ("%d",&i);
  itoa (i,buffer,10);
  printf ("decimal: %s\n",buffer);
  itoa (i,buffer,16);
  printf ("hexadecimal: %s\n",buffer);
  itoa (i,buffer,2);
  printf ("binary: %s\n",buffer);
  return 0;
}

Production:

Enter a number: 1750
decimal: 1750
hexadecimal: 6d6
binary: 11011010110

J'espère que cela t'aides!


1
Hmmm, compiler ça sur Debian me donne "une référence indéfinie à« itoa »». Peut-être que quelque chose ne va pas avec mon système.
Adam Pierce

J'obtiens la même chose sur Ubuntu 8.04. Je ne trouve aucune référence à itoa dans stdio.h ou stdlib.h non plus (pas étonnant car il ne fait pas partie de la norme)
camh

édité pour l'exactitude, merci les gars! désolé, j'oublie toujours que ce n'est pas une boîte Linux vanille ;-)
Matt J

J'ai édité la réponse pour inclure l'argument de taille de tampon; Je crois que tout est comme il se doit maintenant, je ne vois pas de problème avec l'ordre des arguments en soi. Est-ce que je manque quelque chose?
Matt J

Cela ne fonctionne pas pour Linux? quel est le résultat de la question / réponse (non norme semble être tous les systèmes Linux?)

12

Si vous l'appelez beaucoup, le conseil de "juste utiliser snprintf" peut être ennuyeux. Alors, voici ce que vous voulez probablement:

const char *my_itoa_buf(char *buf, size_t len, int num)
{
  static char loc_buf[sizeof(int) * CHAR_BITS]; /* not thread safe */

  if (!buf)
  {
    buf = loc_buf;
    len = sizeof(loc_buf);
  }

  if (snprintf(buf, len, "%d", num) == -1)
    return ""; /* or whatever */

  return buf;
}

const char *my_itoa(int num)
{ return my_itoa_buf(NULL, 0, num); }

8
comme le commentaire dit :)
James Antill

17
Ce n'est pas seulement non-thread-safe, ce n'est pas du tout sûr: - void some_func (char * a, char * b); some_func (itoa (123), itoa (456)); Vous voulez deviner ce que reçoit la fonction?
jcoder

De plus, les constqualificatifs ne font rien sur les types de retour de fonction - vous le sauriez si vous avez activé les avertissements du compilateur :)
cat

3
@cat Mais il n'y a pas de types de retour qualifiés const ici. const char *est un pointeur non const vers const, ce qui a beaucoup de sens et est correct.
Chortos-2

1
@ Chortos-2 C'est intéressant, vous avez bien sûr tout à fait raison - je n'ai pas réalisé la différence sémantique de sens constentre const int f (void) { ...et const int* f (void) { ..., mais maintenant, après l'avoir essayé avec un compilateur, cela a du sens.
chat du

11

itoan'est pas une fonction C standard. Vous pouvez mettre en œuvre le vôtre. Il est apparu dans la première édition de Kernighan et Ritchie's The C Programming Language , à la page 60. La deuxième édition de The C Programming Language («K & R2») contient l'implémentation suivante de itoa, à la page 64. Le livre note plusieurs problèmes avec cette implémentation , y compris le fait qu'il ne gère pas correctement le nombre le plus négatif

 /* itoa:  convert n to characters in s */
 void itoa(int n, char s[])
 {
     int i, sign;

     if ((sign = n) < 0)  /* record sign */
         n = -n;          /* make n positive */
     i = 0;
     do {       /* generate digits in reverse order */
         s[i++] = n % 10 + '0';   /* get next digit */
     } while ((n /= 10) > 0);     /* delete it */
     if (sign < 0)
         s[i++] = '-';
     s[i] = '\0';
     reverse(s);
}  

La fonction reverseutilisée ci-dessus est implémentée deux pages plus tôt:

 #include <string.h>

 /* reverse:  reverse string s in place */
 void reverse(char s[])
 {
     int i, j;
     char c;

     for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
         c = s[i];
         s[i] = s[j];
         s[j] = c;
     }
}  

8

Edit: Je viens de découvrir std::to_stringce qui est identique en fonctionnement à ma propre fonction ci-dessous. Il a été introduit dans C ++ 11 et est disponible dans les versions récentes de gcc, au moins dès 4.5 si vous activez les extensions c ++ 0x.


Non seulement il itoamanque dans gcc, mais ce n'est pas la fonction la plus pratique à utiliser puisque vous devez lui fournir un tampon. J'avais besoin de quelque chose qui puisse être utilisé dans une expression, alors j'ai trouvé ceci:

std::string itos(int n)
{
   const int max_size = std::numeric_limits<int>::digits10 + 1 /*sign*/ + 1 /*0-terminator*/;
   char buffer[max_size] = {0};
   sprintf(buffer, "%d", n);
   return std::string(buffer);
}

Normalement, il serait plus sûr à utiliser snprintfau lieu de sprintfmais le tampon est soigneusement dimensionné pour être immunisé contre le dépassement.

Voir un exemple: http://ideone.com/mKmZVE


12
La question semble concerner C, qui n'a rien std::, etc.
glglgl

6

Comme l'a écrit Matt J, il y en a itoa, mais ce n'est pas standard. Votre code sera plus portable si vous l'utilisez snprintf.


4

La fonction suivante alloue juste assez de mémoire pour conserver la représentation sous forme de chaîne du nombre donné, puis écrit la représentation sous forme de chaîne dans cette zone en utilisant la sprintfméthode standard .

char *itoa(long n)
{
    int len = n==0 ? 1 : floor(log10l(labs(n)))+1;
    if (n<0) len++; // room for negative sign '-'

    char    *buf = calloc(sizeof(char), len+1); // +1 for null
    snprintf(buf, len+1, "%ld", n);
    return   buf;
}

N'oubliez pas d' freeaugmenter la mémoire allouée en cas de besoin:

char *num_str = itoa(123456789L);
// ... 
free(num_str);

NB Comme snprintf copie n-1 octets, nous devons appeler snprintf (buf, len + 1, "% ld", n) (pas seulement snprintf (buf, len, "% ld", n))


4
Ce n'est pas une bonne idée d'appeler votre fonction itoamais de lui donner un comportement différent de ce que les implémentations courantes itoaont réellement. Cette fonction est une bonne idée, mais appelez-la autrement :) Je suggérerais également d'utiliser snprintfpour calculer la longueur du tampon au lieu de la chaîne à virgule flottante; la virgule flottante peut avoir des inexactitudes de cas d'angle. Et ne lancez pas de calloc
MM

Merci pour vos suggestions.
mmdemirbas

Cela devrait être utilisé labssi cela prend un long entier. Sinon, cela pourrait tronquer.
Schwern

snprintfdans un tampon tmp de taille fixe comme char buf[64]pour obtenir la longueur, puis malloccopiez-y. Vous ne tirez aucun avantage de callocover malloc, puisque vous écrivez tous les octets. La copie supplémentaire d'une chaîne très courte est moins mauvaise que d'avoir à appeler le journal en virgule flottante10. Une approximation rapide avec un entier log2 pourrait être utile, cependant, si vous avez une fonction d'analyse de bits qui sera intégrée de manière fiable à quelque chose d'efficace (comme bsrsur x86). (Alternative: mallocun tampon de 64 octets, puis une reallocfois que vous connaissez la longueur finale.)
Peter Cordes

3

Où est la fonction itoa sous Linux?

Une telle fonction n'existe pas sous Linux. J'utilise ce code à la place.

/*
=============
itoa

Convert integer to string

PARAMS:
- value     A 64-bit number to convert
- str       Destination buffer; should be 66 characters long for radix2, 24 - radix8, 22 - radix10, 18 - radix16.
- radix     Radix must be in range -36 .. 36. Negative values used for signed numbers.
=============
*/

char* itoa (unsigned long long  value,  char str[],  int radix)
{
    char        buf [66];
    char*       dest = buf + sizeof(buf);
    boolean     sign = false;

    if (value == 0) {
        memcpy (str, "0", 2);
        return str;
    }

    if (radix < 0) {
        radix = -radix;
        if ( (long long) value < 0) {
            value = -value;
            sign = true;
        }
    }

    *--dest = '\0';

    switch (radix)
    {
    case 16:
        while (value) {
            * --dest = '0' + (value & 0xF);
            if (*dest > '9') *dest += 'A' - '9' - 1;
            value >>= 4;
        }
        break;
    case 10:
        while (value) {
            *--dest = '0' + (value % 10);
            value /= 10;
        }
        break;

    case 8:
        while (value) {
            *--dest = '0' + (value & 7);
            value >>= 3;
        }
        break;

    case 2:
        while (value) {
            *--dest = '0' + (value & 1);
            value >>= 1;
        }
        break;

    default:            // The slow version, but universal
        while (value) {
            *--dest = '0' + (value % radix);
            if (*dest > '9') *dest += 'A' - '9' - 1;
            value /= radix;
        }
        break;
    }

    if (sign) *--dest = '-';

    memcpy (str, dest, buf +sizeof(buf) - dest);
    return str;
}

Vous devez modifier votre réponse pour expliquer comment ce code répond à la question.
C. Helling

2

j'ai essayé ma propre implémentation d'itoa (), il semble fonctionner en binaire, octal, décimal et hexadécimal

#define INT_LEN (10)
#define HEX_LEN (8)
#define BIN_LEN (32)
#define OCT_LEN (11)

static char *  my_itoa ( int value, char * str, int base )
{
    int i,n =2,tmp;
    char buf[BIN_LEN+1];


    switch(base)
    {
        case 16:
            for(i = 0;i<HEX_LEN;++i)
            {
                if(value/base>0)
                {
                    n++;
                }
            }
            snprintf(str, n, "%x" ,value);
            break;
        case 10:
            for(i = 0;i<INT_LEN;++i)
            {
                if(value/base>0)
                {
                    n++;
                }
            }
            snprintf(str, n, "%d" ,value);
            break;
        case 8:
            for(i = 0;i<OCT_LEN;++i)
            {
                if(value/base>0)
                {
                    n++;
                }
            }
            snprintf(str, n, "%o" ,value);
            break;
        case 2:
            for(i = 0,tmp = value;i<BIN_LEN;++i)
            {
                if(tmp/base>0)
                {
                    n++;
                }
                tmp/=base;
            }
            for(i = 1 ,tmp = value; i<n;++i)
            {
                if(tmp%2 != 0)
                {
                    buf[n-i-1] ='1';
                }
                else
                {
                    buf[n-i-1] ='0';
                }
                tmp/=base;
            }
            buf[n-1] = '\0';
            strcpy(str,buf);
            break;
        default:
            return NULL;
    }
    return str;
}

1

copie directe dans le tampon: entier 64 bits itoa hexadécimal:

    char* itoah(long num, char* s, int len)
    {
            long n, m = 16;
            int i = 16+2;
            int shift = 'a'- ('9'+1);


            if(!s || len < 1)
                    return 0;

            n = num < 0 ? -1 : 1;
            n = n * num;

            len = len > i ? i : len;
            i = len < i ? len : i;

            s[i-1] = 0;
            i--;

            if(!num)
            {
                    if(len < 2)
                            return &s[i];

                    s[i-1]='0';
                    return &s[i-1];
            }

            while(i && n)
            {
                    s[i-1] = n % m + '0';

                    if (s[i-1] > '9')
                            s[i-1] += shift ;

                    n = n/m;
                    i--;
            }

            if(num < 0)
            {
                    if(i)
                    {
                            s[i-1] = '-';
                            i--;
                    }
            }

            return &s[i];
    }

Remarque: changez long en long long pour une machine 32 bits. long à int dans le cas d'un entier 32 bits. m est la base. En diminuant la base, augmentez le nombre de caractères (variable i). Lorsque vous augmentez la base, diminuez le nombre de caractères (mieux). En cas de type de données non signé, i devient simplement 16 + 1.


1

Voici une version bien améliorée de la solution d'Archana. Cela fonctionne pour toutes les radix 1-16 et les nombres <= 0, et cela ne devrait pas écraser la mémoire.

static char _numberSystem[] = "0123456789ABCDEF";
static char _twosComp[] = "FEDCBA9876543210";

static void safestrrev(char *buffer, const int bufferSize, const int strlen)
{
    int len = strlen;
    if (len > bufferSize)
    {
        len = bufferSize;
    }
    for (int index = 0; index < (len / 2); index++)
    {
        char ch = buffer[index];
        buffer[index] = buffer[len - index - 1];
        buffer[len - index - 1] = ch;
    }
}

static int negateBuffer(char *buffer, const int bufferSize, const int strlen, const int radix)
{
    int len = strlen;
    if (len > bufferSize)
    {
        len = bufferSize;
    }
    if (radix == 10)
    {
        if (len < (bufferSize - 1))
        {
            buffer[len++] = '-';
            buffer[len] = '\0';
        }
    }
    else
    {
        int twosCompIndex = 0;
        for (int index = 0; index < len; index++)
        {
            if ((buffer[index] >= '0') && (buffer[index] <= '9'))
            {
                twosCompIndex = buffer[index] - '0';
            }
            else if ((buffer[index] >= 'A') && (buffer[index] <= 'F'))
            {
                twosCompIndex = buffer[index] - 'A' + 10;
            }
            else if ((buffer[index] >= 'a') && (buffer[index] <= 'f'))
            {
                twosCompIndex = buffer[index] - 'a' + 10;
            }
            twosCompIndex += (16 - radix);
            buffer[index] = _twosComp[twosCompIndex];
        }
        if (len < (bufferSize - 1))
        {
            buffer[len++] = _numberSystem[radix - 1];
            buffer[len] = 0;
        }
    }
    return len;
}

static int twosNegation(const int x, const int radix)
{
    int n = x;
    if (x < 0)
    {
        if (radix == 10)
        {
            n = -x;
        }
        else
        {
            n = ~x;
        }
    }
    return n;
}

static char *safeitoa(const int x, char *buffer, const int bufferSize, const int radix)
{
    int strlen = 0;
    int n = twosNegation(x, radix);
    int nuberSystemIndex = 0;

    if (radix <= 16)
    {
        do
        {
            if (strlen < (bufferSize - 1))
            {
                nuberSystemIndex = (n % radix);
                buffer[strlen++] = _numberSystem[nuberSystemIndex];
                buffer[strlen] = '\0';
                n = n / radix;
            }
            else
            {
                break;
            }
        } while (n != 0);
        if (x < 0)
        {
            strlen = negateBuffer(buffer, bufferSize, strlen, radix);
        }
        safestrrev(buffer, bufferSize, strlen);
        return buffer;
    }
    return NULL;
}

1

Si vous souhaitez simplement les imprimer:

void binary(unsigned int n)
{
    for(int shift=sizeof(int)*8-1;shift>=0;shift--)
    {
       if (n >> shift & 1)
         printf("1");
       else
         printf("0");

    }
    printf("\n");
} 

1

Lire le code des gars qui le font pour gagner leur vie vous donnera un LONG CHEMIN.

Découvrez comment les gars de MySQL l'ont fait. La source est TRÈS BIEN COMMENTEE et vous apprendra bien plus que des solutions piratées trouvées partout.

Implémentation MySQL de int2str

Je fournis la mise en œuvre mentionnée ici; le lien est ici pour référence et doit être utilisé pour lire l'implémentation complète.

char *
int2str(long int val, char *dst, int radix, 
        int upcase)
{
  char buffer[65];
  char *p;
  long int new_val;
  char *dig_vec= upcase ? _dig_vec_upper : _dig_vec_lower;
  ulong uval= (ulong) val;

  if (radix < 0)
  {
    if (radix < -36 || radix > -2)
      return NullS;
    if (val < 0)
    {
      *dst++ = '-';
      /* Avoid integer overflow in (-val) for LLONG_MIN (BUG#31799). */
      uval = (ulong)0 - uval;
    }
    radix = -radix;
  }
  else if (radix > 36 || radix < 2)
    return NullS;

  /*
    The slightly contorted code which follows is due to the fact that
    few machines directly support unsigned long / and %.  Certainly
    the VAX C compiler generates a subroutine call.  In the interests
    of efficiency (hollow laugh) I let this happen for the first digit
    only; after that "val" will be in range so that signed integer
    division will do.  Sorry 'bout that.  CHECK THE CODE PRODUCED BY
    YOUR C COMPILER.  The first % and / should be unsigned, the second
    % and / signed, but C compilers tend to be extraordinarily
    sensitive to minor details of style.  This works on a VAX, that's
    all I claim for it.
  */
  p = &buffer[sizeof(buffer)-1];
  *p = '\0';
  new_val= uval / (ulong) radix;
  *--p = dig_vec[(uchar) (uval- (ulong) new_val*(ulong) radix)];
  val = new_val;
  while (val != 0)
  {
    ldiv_t res;
    res=ldiv(val,radix);
    *--p = dig_vec[res.rem];
    val= res.quot;
  }
  while ((*dst++ = *p++) != 0) ;
  return dst-1;
}

1
Un lien vers une solution potentielle est toujours le bienvenu, mais veuillez ajouter du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là. Citez toujours la partie la plus pertinente d'un lien important, au cas où le site cible serait inaccessible ou serait définitivement hors ligne. Tenez compte du fait qu'être à peine plus qu'un lien vers un site externe est une raison possible pour savoir pourquoi et comment certaines réponses sont-elles supprimées? .
Tunaki

1
Alors, qu'est-ce qui est si bon dans l'extrait que vous avez publié ici? À quoi doivent faire attention les futurs lecteurs?
Martijn Pieters

1

Où est la fonction itoa sous Linux?

Comme ce itoa()n'est pas standard en C, il existe différentes versions avec différentes signatures de fonction.
char *itoa(int value, char *str, int base);est courant dans * nix.

S'il est absent de Linux ou si le code ne veut pas limiter la portabilité, le code pourrait se l'approprier.

Voici une version qui n'a pas de problème avec INT_MINet gère les tampons à problèmes: NULLou un tampon insuffisant retourne NULL.

#include <stdlib.h>
#include <limits.h>
#include <string.h>

// Buffer sized for a decimal string of a `signed int`, 28/93 > log10(2)
#define SIGNED_PRINT_SIZE(object)  ((sizeof(object) * CHAR_BIT - 1)* 28 / 93 + 3)

char *itoa_x(int number, char *dest, size_t dest_size) {
  if (dest == NULL) {
    return NULL;
  }

  char buf[SIGNED_PRINT_SIZE(number)];
  char *p = &buf[sizeof buf - 1];

  // Work with negative absolute value
  int neg_num = number < 0 ? number : -number;

  // Form string
  *p = '\0';
  do {
    *--p = (char) ('0' - neg_num % 10);
    neg_num /= 10;
  } while (neg_num);
  if (number < 0) {
    *--p = '-';
  }

  // Copy string
  size_t src_size = (size_t) (&buf[sizeof buf] - p);
  if (src_size > dest_size) {
    // Not enough room
    return NULL;
  }
  return memcpy(dest, p, src_size);
}

Vous trouverez ci-dessous une version C99 ou ultérieure qui gère n'importe quelle base [2 ... 36]

char *itoa_x(int number, char *dest, size_t dest_size, int base) {
  if (dest == NULL || base < 2 || base > 36) {
    return NULL;
  }

  char buf[sizeof number * CHAR_BIT + 2]; // worst case: itoa(INT_MIN,,,2)
  char *p = &buf[sizeof buf - 1];

  // Work with negative absolute value to avoid UB of `abs(INT_MIN)`
  int neg_num = number < 0 ? number : -number;

  // Form string
  *p = '\0';
  do {
    *--p = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-(neg_num % base)];
    neg_num /= base;
  } while (neg_num);
  if (number < 0) {
    *--p = '-';
  }

  // Copy string
  size_t src_size = (size_t) (&buf[sizeof buf] - p);
  if (src_size > dest_size) {
    // Not enough room
    return NULL;
  }
  return memcpy(dest, p, src_size);
}

Pour un code compatible C89 et ultérieur, remplacez la boucle interne par

  div_t qr;
  do {
    qr = div(neg_num, base);
    *--p = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-qr.rem];
    neg_num = qr.quot;
  } while (neg_num);

1

implémentation interne de la glibc

la glibc 2.28 a une implémentation interne:

qui est utilisé à plusieurs endroits en interne, mais je n'ai pas pu trouver s'il peut être exposé ou comment.

Au moins, cela devrait être une implémentation robuste si vous êtes prêt à l'extraire.

Cette question demande comment rouler le vôtre: Comment convertir un int en chaîne en C?


1

Je préférerais ceci: https://github.com/wsq003/itoa_for_linux

Ce devrait être l'itoa () le plus rapide de tous les temps. Nous utilisons itoa () au lieu de sprintf () pour des raisons de performances, donc un itoa () le plus rapide avec des fonctionnalités limitées est raisonnable et utile.


0

J'ai utilisé _itoa (...) sur RedHat 6 et le compilateur GCC. Ça marche.


0

Le remplacement par snprintf n'est PAS terminé!

Il ne couvre que les bases: 2, 8, 10, 16, alors que itoa fonctionne pour les bases entre 2 et 36.

Depuis que je cherchais un remplacement pour la base 32, je suppose que je vais devoir coder le mien!


-4

Vous pouvez utiliser ce programme au lieu de sprintf.

void itochar(int x, char *buffer, int radix);

int main()
{
    char buffer[10];
    itochar(725, buffer, 10);
    printf ("\n %s \n", buffer);
    return 0;
}

void itochar(int x, char *buffer, int radix)
{
    int i = 0 , n,s;
    n = s;
    while (n > 0)
    {
        s = n%radix;
        n = n/radix;
        buffer[i++] = '0' + s;
    }
    buffer[i] = '\0';
    strrev(buffer);
}

4
Il y a tellement de bogues dans ce code: 1) Ne convertit pas réellement l'hexagone correctement. 2) Ne convertit pas du tout 0. 3) Ne fonctionne pas avec des nombres négatifs. 4) Aucune vérification du dépassement de tampon. Je publierai sous peu une version améliorée de ce code.
Chris Desjardins
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.