C int bir dizeye dönüştürmek nasıl?


225

Nasıl dönüştürür int (tamsayı) bir dizeye ? Ben structbir dosyaya kaydetmek için bir dizeye veri dönüştüren bir işlev yapmaya çalışıyorum .


3
printfya da kuzenlerinden biri hile yapmalı
pmg


1
serileştirme ile ilgili bu SSS'yi ve belki de C'deki serileştirme ile ilgili aşağıdaki soruları görmek isteyebilirsiniz : (a) , (b) , (c) asıl amacınıza ulaşmak için.
moooeeeep

2
Burada her zamanki evcil hayvan semantik tedirginim. Hiçbir şeyi dönüştürmek istemezsiniz; değerinin bir (base 10?) temsilini içeren bir dize elde etmek istiyorsunuz int. Evet biliyorum. Bu çok yaygın bir kısa yol, ama yine de beni rahatsız ediyor.
dmckee --- eski moderatör yavru kedi

olası bir kopyasını int c dizeye dönüştürme
nawfal

Yanıtlar:


92

DÜZENLEME: Yorumda belirtildiği gibi, itoa()bir standart değildir, bu yüzden rakip cevapta önerilen sprintf () yaklaşımını daha iyi kullanın!


Tamsayı değerinizi bir dizeye dönüştürmek için itoa()işlevi kullanabilirsiniz .

İşte bir örnek:

int num = 321;
char snum[5];

// convert 123 to string [buf]
itoa(num, snum, 10);

// print our string
printf("%s\n", snum);

Yapınızı bir dosyaya çıktılamak istiyorsanız, önceden herhangi bir değeri dönüştürmenize gerek yoktur. Yalnızca değerlerinizin nasıl çıktı verileceğini belirtmek için printf formatı belirtimini kullanabilir ve verilerinizin çıktısını almak için printf ailesindeki operatörlerden herhangi birini kullanabilirsiniz .


30
itoastandart değildir - bakınız örn. stackoverflow.com/questions/190229/…
Paul R

1
@PaulR Şimdi bunu bilmiyordum! Açıklama için teşekkürler.
Christian Rau

1
@PaulR Teşekkürler, bunu bilmiyordum!
Alexander Galkin

@SeanRamey Bu itoa(), aynı arabellek taşması potansiyeline sahiptir gets().
chux - Monica'yı

itoa C'de mevcut değildir (en azından C99). C ++
Motti Shneor'un

211

Bunu sprintfyapmak için veya belki snprintfde sahipseniz kullanabilirsiniz:

char str[ENOUGH];
sprintf(str, "%d", 42);

Burada karakter sayısı (artı sonlandırıcı karakter) strkullanılarak hesaplanabilir:

(int)((ceil(log10(num))+1)*sizeof(char))

9
Tat olduğundan emin olmak ENOUGHiçin bunu yapabilirizmalloc(sizeof(char)*(int)log10(num))
Hauleth

2
Hatta 2 @Hauleth Ya düşünüyor (int)log10(42)olduğunu 1.
Christian Rau

23
Veya bunu derleme zamanında hesaplayabilirsiniz:#define ENOUGH ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
caf

4
@hauleth Ceil ile bile +1 yerine hala +2: ceil (log (100)) = 2.0, 3 değil. Yani 10'un tam güçleri için +1 ve null'u sonlandırmak için başka bir +1.
not-just-yeti

24
Eksi işareti ve yerel ayarı dikkate almazsınız: binlerce ayırıcı ve gruplama. Lütfen şu şekilde yapın: int length = snprintf(NULL, 0,"%d",42);uzunluk almak için kullanın ve sonra length+1dize için karakter ayırın .
user2622016

70

Kısa cevap:

snprintf( str, size, "%d", x );

Daha uzun: ilk önce yeterli boyutu bulmanız gerekir. ilk parametre olarak snprintfçağırırsanız uzunluğunu bildirir NULL, 0:

snprintf( NULL, 0, "%d", x );

Boş sonlandırıcı için bir karakter daha ayırın.

#include <stdio.h> 
#include <stdlib.h>

int x = -42;
int length = snprintf( NULL, 0, "%d", x );
char* str = malloc( length + 1 );
snprintf( str, length + 1, "%d", x );
...
free(str);

Her biçim dizesi için çalışıyorsa, kullanarak float veya double öğesini dizeye "%g"dönüştürebilirsiniz, int komutunu kullanarak hex'e dönüştürebilirsiniz "%x", vb.


3
#include <stdio.h> #include <stdlib.h>
user1821961

@ user1821961 Teşekkürler, bu başlık dosyalarının dahil edilmesi gerektiği belirtilmelidir.
byxor

Bunu, kitap tarafından "en sağlam ve" en sağlam şekilde seviyorum.
Motti Shneor

30

Gcc için itoa'nın çeşitli sürümlerine baktıktan sonra, ikili, ondalık ve onaltılık dönüşümleri gerçekleştirebilen en esnek sürüm, hem olumlu hem de negatif http://www.strudel.org adresinde bulunan dördüncü sürümdür. .uk / itoa / . İken sprintf/ snprintfavantajlara sahip, bunlar ondalık dönüşüm dışında herhangi bir amaçla negatif sayılar ele olmayacaktır. Yukarıdaki bağlantı çevrimdışı olduğundan veya artık etkin olmadığından, 4. sürümlerini aşağıya ekledim:

/**
 * C++ version 0.4 char* style "itoa":
 * Written by Lukás Chmela
 * Released under GPLv3.
 */
char* itoa(int value, char* result, int base) {
    // check that the base if valid
    if (base < 2 || base > 36) { *result = '\0'; return result; }

    char* ptr = result, *ptr1 = result, tmp_char;
    int tmp_value;

    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
    } while ( value );

    // Apply negative sign
    if (tmp_value < 0) *ptr++ = '-';
    *ptr-- = '\0';
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }
    return result;
}

4
Ayrıca, bu sprintf'den oldukça hızlıdır. Büyük dosyaları dökerken önemli olabilir.
Eugene Ryabtsev

@Eugene Ryabtsev C, performans derecesini belirtmez sprintf()ve "bu, sprintf'den oldukça hızlıdır" derleyicinizde doğru olabilir, ancak her zaman beklemez. Bir derleyici, bu kullanıcıdan daha hızlı sprintf(str, "%d", 42);bir şekilde optimize edilmiş bir itoa()işleve bakabilir ve optimize edebilir . kodu.
chux - Monica'yı eski durumuna döndür

3
@chux Evet, bir derleyici sprintf(str, "%d", 42);iki sabit grafik ekleyerek optimizasyon yapabilir , ancak bu teoridir. Pratikte insanlar const in sprintf sprint değil ve yukarıdaki itoa neredeyse alır gibi optimize edilmiştir. En azından, genel bir sprintf'in büyüklüğünün düşürülme emirlerini almayacağınızdan% 100 emin olabilirsiniz. Derleyici sürümü ve ayarlarıyla aklınızda ne olursa olsun örnek görmek güzel olurdu.
Eugene Ryabtsev

1
@chux Bir çağrı ile sonuçlanırsa, çağrılan rutini yukarıdaki rutinle karşılaştırmadıkça (daha fazla çağrıya kadar; montajda, isterseniz) fazla bir şey açıklamaz. Derleyici sürümü ve ayarlarıyla bir cevap olarak yaparsanız, yararlı olarak değerlendireceğim.
Eugene Ryabtsev

2
Çıktı uzunluğunu geri almak için bir yol sağlamak iyi olur. Bunu burada yaptım: stackoverflow.com/a/52111436/895245 + birim testleri.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

9

Bu eski ama işte başka bir yol.

#include <stdio.h>

#define atoa(x) #x

int main(int argc, char *argv[])
{
    char *string = atoa(1234567890);
    printf("%s\n", string);
    return 0;
}

32
Değişkenler için değil, yalnızca sabitler için çalışır.
Nakedible

7

GCC kullanıyorsanız, GNU uzantı asprintf işlevini kullanabilirsiniz.

char* str;
asprintf (&str, "%i", 12313);
free(str);

7

Bir şeyi bir dizgeye dönüştürmek, 1) sonuçtaki dizgiyi tahsis etmeli veya 2) bir char *hedef ve boyutta geçmelidir . Aşağıdaki örnek kod:

Her ikisi de intdahil herkes için çalışır INT_MIN. snprintf()Geçerli yerel ayara bağlı olarak tutarlı bir çıktı sağlarlar .

Yöntem 1: NULLBellek yetersiz döndürür .

#define INT_DECIMAL_STRING_SIZE(int_type) ((CHAR_BIT*sizeof(int_type)-1)*10/33+3)

char *int_to_string_alloc(int x) {
  int i = x;
  char buf[INT_DECIMAL_STRING_SIZE(int)];
  char *p = &buf[sizeof buf - 1];
  *p = '\0';
  if (i >= 0) {
    i = -i;
  }
  do {
    p--;
    *p = (char) ('0' - i % 10);
    i /= 10;
  } while (i);
  if (x < 0) {
    p--;
    *p = '-';
  }
  size_t len = (size_t) (&buf[sizeof buf] - p);
  char *s = malloc(len);
  if (s) {
    memcpy(s, p, len);
  }
  return s;
}

Yöntem 2: NULLArabellek çok küçükse döndürür .

static char *int_to_string_helper(char *dest, size_t n, int x) {
  if (n == 0) {
    return NULL;
  }
  if (x <= -10) {
    dest = int_to_string_helper(dest, n - 1, x / 10);
    if (dest == NULL) return NULL;
  }
  *dest = (char) ('0' - x % 10);
  return dest + 1;
}

char *int_to_string(char *dest, size_t n, int x) {
  char *p = dest;
  if (n == 0) {
    return NULL;
  }
  n--;
  if (x < 0) {
    if (n == 0) return NULL;
    n--;
    *p++ = '-';
  } else {
    x = -x;
  }
  p = int_to_string_helper(p, n, x);
  if (p == NULL) return NULL;
  *p = 0;
  return dest;
}

@Alter Mann tarafından istek üzerine [Düzenle]

(CHAR_BIT*sizeof(int_type)-1)*10/33+3en azından charbazı işaretli tam sayı türünü isteğe bağlı negatif işaret, rakam ve boş karakterden oluşan bir dize olarak kodlamak için gereken maksimum sayıdır.

İşaretli bir tamsayıdaki işaretsiz bit sayısı en fazla CHAR_BIT*sizeof(int_type)-1. n-Bit ikili sayının temel-10 temsili n*log10(2) + 1rakam alır . 10/33biraz daha fazladır log10(2). İşaret chariçin +1 ve boş karakter için +1. Diğer fraksiyonlar 28/93 gibi kullanılabilir.


Yöntem 3: Bir kişi kenarda yaşamak istiyorsa ve arabellek taşması endişe verici değilse, hepsini işleyen basit bir C99 veya sonraki bir çözüm gelir int.

#include <limits.h>
#include <stdio.h>

static char *itoa_simple_helper(char *dest, int i) {
  if (i <= -10) {
    dest = itoa_simple_helper(dest, i/10);
  }
  *dest++ = '0' - i%10;
  return dest;
}

char *itoa_simple(char *dest, int i) {
  char *s = dest;
  if (i < 0) {
    *s++ = '-';
  } else {
    i = -i;
  }
  *itoa_simple_helper(s, i) = '\0';
  return dest;
}

int main() {
  char s[100];
  puts(itoa_simple(s, 0));
  puts(itoa_simple(s, 1));
  puts(itoa_simple(s, -1));
  puts(itoa_simple(s, 12345));
  puts(itoa_simple(s, INT_MAX-1));
  puts(itoa_simple(s, INT_MAX));
  puts(itoa_simple(s, INT_MIN+1));
  puts(itoa_simple(s, INT_MIN));
}

Örnek çıktı

0
1
-1
12345
2147483646
2147483647
-2147483647
-2147483648

Hey chux, glibc iç uygulamasını ortaya çıkarmanın herhangi bir yolu olup olmadığını biliyor musunuz? sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/...
Ciro Santilli郝海东冠状病六四事件法轮功

1
@CiroSantilli g 改造 中心 六四 事件 g glibc iç uygulamasına aşina değilim.
chux - Monica'yı yeniden başlat

3
/*Function return size of string and convert signed  *
 *integer to ascii value and store them in array of  *
 *character with NULL at the end of the array        */

int itoa(int value,char *ptr)
     {
        int count=0,temp;
        if(ptr==NULL)
            return 0;   
        if(value==0)
        {   
            *ptr='0';
            return 1;
        }

        if(value<0)
        {
            value*=(-1);    
            *ptr++='-';
            count++;
        }
        for(temp=value;temp>0;temp/=10,ptr++);
        *ptr='\0';
        for(temp=value;temp>0;temp/=10)
        {
            *--ptr=temp%10+'0';
            count++;
        }
        return count;
     }

2

Yapınızı bir dosyaya çıkarmak istiyorsanız, önceden herhangi bir değeri dönüştürmenize gerek yoktur. Yalnızca değerlerinizin nasıl çıktı verileceğini belirtmek için printf formatı belirtimini kullanabilir ve verilerinizin çıktısını almak için printf ailesindeki operatörlerden herhangi birini kullanabilirsiniz.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.