Як перетворити int в рядок в C?


225

Як перетворити int(ціле) на рядок? Я намагаюся зробити функцію, яка перетворює дані a structв рядок, щоб зберегти його у файлі.


3
printfабо хтось із його двоюрідних братів повинен зробити трюк
pmg

1
Можливий дублікат Де функція itoa в Linux?
Пол Р

1
Ви також можете побачити цей FAQ щодо серіалізації та, можливо, наступні питання, що стосуються серіалізації в C: (a) , (b) , (c) для досягнення вашого фактичного наміру.
moooeeeep

2
Мій звичайний домашній улюбленець семантичний пій. Ви не хочете нічого перетворювати; ви хочете отримати рядок, що містить (базу 10?) подання значення int. Так, я знаю. Це дуже поширена коротка шортка, але вона все ще клопоче мене.
dmckee --- кошеня колишнього модератора

можливий дублікат Перетворення int в рядок в c
nawfal

Відповіді:


92

EDIT: Як зазначено в коментарі, itoa()це не стандарт, тому краще використовувати підхід sprintf (), запропонований у відповіді, що конкурує!


Ви можете використовувати itoa()функцію для перетворення цілого значення в рядок.

Ось приклад:

int num = 321;
char snum[5];

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

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

Якщо ви хочете вивести свою структуру у файл, не потрібно заздалегідь конвертувати якесь значення. Ви можете просто використовувати специфікацію формату printf, щоб вказати, як вивести свої значення та використовувати будь-якого оператора з сім'ї printf для виведення даних.


29
itoaне є стандартним - див., наприклад, stackoverflow.com/questions/190229/…
Paul R

1
@PaulR Тепер я цього не знав! Дякуємо за роз’яснення.
Крістіан Рау

1
@PaulR Спасибі, я цього не знав!
Олександр Галкін

@SeanRamey Це itoa()страждає тим же потенціалом переповнення буфера, що і gets().
chux

itoa недоступний у С (принаймні C99). він доступніший в C ++
Motti Shneor

211

Ви можете використовувати sprintfце, а може, snprintfякщо у вас є:

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

Де кількість символів (плюс кінцевий знак) у банці strможе бути обчислена, використовуючи:

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

9
Щоб бути впевненим, що ENOUGHцього досить, ми можемо зробити цеmalloc(sizeof(char)*(int)log10(num))
Hauleth

2
@Hauleth Або навіть +2, вважаючи, що (int)log10(42)це 1.
Крістіан Рау

23
Або ви можете обчислити його в час компіляції:#define ENOUGH ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
caf

4
@hauleth Still +2 замість +1, навіть з ceil: ceil (log (100)) = 2.0, не 3. Так +1 для точної потужності-10, а ще +1 для завершення нуля.
не-просто-yeti

24
Ви не враховуєте знак мінусу та локаль: роздільник тисяч та групування. Будь ласка, зробіть це так: використовуйте int length = snprintf(NULL, 0,"%d",42);для отримання довжини, а потім виділіть length+1символи для рядка.
користувач2622016

69

Коротка відповідь:

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

Що довше: спочатку потрібно з’ясувати достатній розмір. snprintfповідомляє довжину, якщо ви називаєте це NULL, 0як перші параметри:

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

Виділіть ще один символ для null-термінатора.

#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);

Якщо працює для кожного рядка формату, тож ви можете перетворити float або double у рядок за допомогою "%g", ви можете перетворити int в hex за допомогою "%x"тощо.


3
#include <stdio.h> #include <stdlib.h>
користувач1821961

@ user1821961 Дякую, справді слід згадати, що ці файли заголовків потрібно включати.
byxor

Мені подобається ця, будучи найбільш надійною і "за книгою".
Motti Shneor

30

Переглянувши різні версії itoa для gcc, я виявив найбільш гнучку версію, яка здатна обробляти перетворення у двійкові, десяткові та шістнадцяткові, і позитивні, і негативні - четверта версія, знайдена на веб- сайті http://www.strudel.org .uk / itoa / . Хоча sprintf/ snprintfмають переваги, вони не оброблятимуть негативні числа для нічого іншого, крім десяткового перетворення. Оскільки вищевказане посилання або офлайн, або більше не активне, я включив їх четверту версію нижче:

/**
 * 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
Також це значно швидше, ніж спринт. Може бути важливим під час завантаження великих файлів.
Євген Рябцев

@Eugene Ryabtsev C не визначає рейтинг продуктивності, sprintf()і "це значно швидше, ніж sprintf" може бути правдою для вашого компілятора, але не завжди має місце. Компілятор може розглянути sprintf(str, "%d", 42);та оптимізувати його під оптимізовану itoa()функцію - звичайно швидше, ніж цей користувач. код.
chux

3
@chux Так, компілятор може оптимізувати sprintf(str, "%d", 42);додавання двох символів const, але це теорія. На практиці люди не спринтерствують, і itaa вище майже оптимізована. Принаймні, ви можете бути на 100% впевнені, що не отримаєте порядків зменшення загального спринту. Було б добре побачити будь-який контрприклад, який ви маєте на увазі, з версією компілятора та налаштуваннями.
Євген Рябцев

1
@chux Якщо він закінчується на виклику, він не пояснює багато, якщо ми не порівнюємо виклик звичайної програми з вищевказаною процедурою (аж до заборони більше, якщо ви хочете). Якщо ви зробите це як відповідь із версією та налаштуваннями компілятора, я визнаю це корисним.
Євген Рябцев

2
Було б добре передбачити спосіб повернути вихідну довжину. Я зробив це тут: stackoverflow.com/a/52111436/895245 + одиничні тести.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

9

Це старе, але ось інший спосіб.

#include <stdio.h>

#define atoa(x) #x

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

32
Працює лише для констант, а не для змінних.
Голий

7

Якщо ви використовуєте GCC, ви можете використовувати функцію розширення GNU asprintf.

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

7

Перетворення чого-небудь у рядок повинно або: 1) виділити результуючу рядок, або 2) передати char *призначення та розмір. Приклад коду нижче:

Обидва працюють для всіх, в intтому числі INT_MIN. Вони забезпечують послідовний вихід на відміну від того, snprintf()що залежить від поточного локалу.

Спосіб 1: Повернення NULLпоза пам'яттю.

#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;
}

Спосіб 2: Він повертається, NULLякщо буфера було занадто мало.

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

(CHAR_BIT*sizeof(int_type)-1)*10/33+3є принаймні максимальною кількістю, charнеобхідною для кодування деякого підписаного цілого типу у вигляді рядка, що складається з необов'язкового негативного знака, цифр та нульового символу.

Кількість беззнакових бітів у цілому підписаному цілому не більше CHAR_BIT*sizeof(int_type)-1. Базове представлення n-бітового бінарного числа -10 доходить до n*log10(2) + 1цифр. 10/33трохи більше, ніж log10(2). +1 для знака charі +1 для нульового символу. Інші фракції можуть бути використані, наприклад, 28/93.


Спосіб 3: Якщо хочеться жити на межі, і переповнення буфера не викликає особливих проблем, випливає просте рішення C99 або новіших версій, яке обробляє всіх 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));
}

Вибірка зразка

0
1
-1
12345
2147483646
2147483647
-2147483647
-2147483648

Ей, чу, чи знаєте ви, чи є можливість виявити внутрішню реалізацію glibc? sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 Я не знайомий із внутрішньою реалізацією glibc.
chux

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

Якщо ви хочете вивести свою структуру у файл, не потрібно заздалегідь конвертувати якесь значення. Ви можете просто використовувати специфікацію формату printf, щоб вказати, як вивести свої значення та використовувати будь-якого оператора з сім'ї printf для виведення даних.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.