Làm thế nào để chuyển đổi một int thành chuỗi trong C?


225

Làm thế nào để bạn chuyển đổi một int(số nguyên) thành một chuỗi? Tôi đang cố gắng tạo một hàm chuyển đổi dữ liệu của structchuỗi thành một chuỗi để lưu nó vào một tệp.


3
printfhoặc một trong những anh em họ của nó nên thực hiện mánh khóe
pmg


1
bạn cũng có thể muốn xem Câu hỏi thường gặp này về tuần tự hóa và có thể các câu hỏi sau liên quan đến tuần tự hóa trong C: (a) , (b) , (c) để đạt được ý định thực sự của bạn.
moooeeeep

2
Thú cưng thông thường của tôi peeve ở đây. Bạn không muốn chuyển đổi bất cứ điều gì; bạn muốn có được một chuỗi chứa một đại diện (cơ sở 10?) của giá trị của int. Vâng, tôi biết. Đó là một lối tắt rất phổ biến, nhưng nó vẫn làm phiền tôi.
dmckee --- ex-moderator mèo con

Câu trả lời:


92

EDIT: Như đã chỉ ra trong nhận xét, itoa()không phải là một tiêu chuẩn, vì vậy tốt hơn nên sử dụng phương pháp sprintf () được đề xuất trong câu trả lời đối thủ!


Bạn có thể sử dụng itoa()hàm để chuyển đổi giá trị số nguyên của mình thành một chuỗi.

Đây là một ví dụ:

int num = 321;
char snum[5];

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

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

Nếu bạn muốn xuất cấu trúc của mình thành một tệp, không cần phải chuyển đổi bất kỳ giá trị nào trước đó. Bạn chỉ có thể sử dụng đặc tả định dạng printf để chỉ ra cách xuất giá trị của bạn và sử dụng bất kỳ toán tử nào từ họ printf để xuất dữ liệu của bạn.


30
itoakhông chuẩn - xem vd: stackoverflow.com/questions/190229/ từ
Paul R

1
@PaulR Bây giờ tôi không biết điều đó! Cảm ơn bạn đã làm rõ.
Christian Rau

1
@PaulR Cảm ơn, tôi không biết điều đó!
Alexander Galkin

@SeanRamey Điều này itoa()có tiềm năng tràn bộ đệm tương tự như gets().
chux - Phục hồi Monica

itoa không có sẵn trong C (ít nhất là C99). nó có sẵn nhiều hơn trong C ++
Motti Shneor

211

Bạn có thể sử dụng sprintfđể làm điều đó hoặc có thể snprintfnếu bạn có nó:

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

Trong đó số lượng ký tự (cộng với chấm dứt char) trong strcó thể được tính bằng cách sử dụng:

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

9
Để chắc chắn rằng tat ENOUGHlà đủ, chúng ta có thể làm điều đó bằng cáchmalloc(sizeof(char)*(int)log10(num))
Hauleth

2
@Hauleth Hoặc thậm chí +2, xem xét đó (int)log10(42)1.
Christian Rau

23
Hoặc bạn có thể tính toán nó vào thời gian biên dịch:#define ENOUGH ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
caf

4
@hauleth Vẫn là +2 thay vì +1, ngay cả với ceil: ceil (log (100)) = 2.0, chứ không phải 3. Vì vậy, +1 cho độ chính xác của 10 và +1 khác để kết thúc null.
không chỉ là

24
Bạn không xem xét dấu trừ và miền địa phương: hàng ngàn dấu phân cách và nhóm. Vui lòng làm theo cách này: sử dụng int length = snprintf(NULL, 0,"%d",42);để lấy độ dài và sau đó phân bổ length+1ký tự cho chuỗi.
dùng2622016

69

Câu trả lời ngắn gọn là:

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

Cái dài hơn là: đầu tiên bạn cần tìm ra đủ kích cỡ. snprintfcho bạn biết chiều dài nếu bạn gọi nó với NULL, 0các tham số đầu tiên:

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

Phân bổ thêm một ký tự cho null-terminator.

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

Nếu hoạt động cho mọi chuỗi định dạng, do đó bạn có thể chuyển đổi float hoặc double thành chuỗi bằng cách sử dụng "%g", bạn có thể chuyển đổi int thành hex bằng cách sử dụng "%x", v.v.


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

@ user1821961 Cảm ơn, thực sự cần phải đề cập rằng các tệp tiêu đề này cần được đưa vào.
byxor

Tôi yêu cái này, là cách mạnh mẽ nhất và "theo cuốn sách".
Motti Shneor

30

Sau khi xem xét các phiên bản khác nhau của itoa cho gcc, phiên bản linh hoạt nhất mà tôi thấy có khả năng xử lý các chuyển đổi thành nhị phân, thập phân và thập lục phân, cả tích cực và tiêu cực là phiên bản thứ tư được tìm thấy tại http://www.strudel.org .uk / itoa / . Mặc dù sprintf/ snprintfcó lợi thế, họ sẽ không xử lý các số âm cho bất kỳ thứ gì ngoài chuyển đổi thập phân. Vì liên kết ở trên là ngoại tuyến hoặc không còn hoạt động, tôi đã bao gồm phiên bản thứ 4 của chúng bên dưới:

/**
 * 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
Ngoài ra, điều này là nhanh hơn đáng kể so với sprintf. Có thể quan trọng khi đổ các tập tin lớn.
Eugene Ryabtsev

@Eugene Ryabtsev C không chỉ định xếp hạng hiệu suất sprintf()và "điều này nhanh hơn đáng kể so với sprintf" có thể đúng trên trình biên dịch của bạn nhưng không phải lúc nào cũng giữ. Một trình biên dịch có thể xem xét sprintf(str, "%d", 42);và tối ưu hóa nó thành một itoa()chức năng giống như được tối ưu hóa - chắc chắn nhanh hơn người dùng này. mã.
chux - Phục hồi Monica

3
@chux Có, một trình biên dịch có thể tối ưu hóa sprintf(str, "%d", 42);khi nối thêm hai ký tự const, nhưng đó là lý thuyết. Trong thực tế mọi người không chạy nước rút const ints và itoa ở trên gần như được tối ưu hóa như nó được. Ít nhất bạn có thể chắc chắn 100% rằng bạn sẽ không nhận được các lệnh hạ cấp độ lớn của một lần chạy nước rút chung. Sẽ thật tuyệt khi thấy bất cứ mẫu nào bạn có trong đầu, với phiên bản và cài đặt trình biên dịch.
Eugene Ryabtsev

1
@chux Nếu nó kết thúc trong một cuộc gọi, nó sẽ không giải thích nhiều trừ khi chúng ta so sánh thói quen được gọi với thói quen ở trên (tất cả đều không có nhiều cuộc gọi nữa, nếu bạn muốn). Nếu bạn làm nó như một câu trả lời với phiên bản và cài đặt trình biên dịch, tôi sẽ nâng cấp nó thành hữu ích.
Eugene Ryabtsev

2
Nó sẽ là tốt để cung cấp một cách để lấy lại chiều dài đầu ra. Tôi đã thực hiện điều đó ở đây: stackoverflow.com/a/52111436/895245 + kiểm tra đơn vị.
Ciro Santilli 郝海东 冠状 病

9

Đây là cũ nhưng đây là một cách khác.

#include <stdio.h>

#define atoa(x) #x

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

32
Chỉ hoạt động cho hằng, không cho biến.
Khỏa thân

7

Nếu bạn đang sử dụng GCC, bạn có thể sử dụng chức năng asprintf mở rộng GNU.

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

7

Chuyển đổi bất cứ thứ gì thành một chuỗi nên 1) phân bổ chuỗi kết quả hoặc 2) truyền vào char *đích và kích thước. Mã mẫu dưới đây:

Cả hai làm việc cho tất cả intbao gồm INT_MIN. Họ cung cấp một đầu ra nhất quán không giống như snprintf()phụ thuộc vào miền địa phương hiện tại.

Phương pháp 1: Trả NULLvề bộ nhớ ngoài.

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

Phương pháp 2: Nó trả về NULLnếu bộ đệm quá nhỏ.

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

[Chỉnh sửa] theo yêu cầu của @Alter Mann

(CHAR_BIT*sizeof(int_type)-1)*10/33+3ít nhất là số lượng tối đa charcần thiết để mã hóa một số loại số nguyên đã ký dưới dạng một chuỗi bao gồm một dấu âm, chữ số và ký tự null tùy chọn ..

Số lượng bit không dấu trong một số nguyên đã ký không nhiều hơn CHAR_BIT*sizeof(int_type)-1. Một đại diện cơ sở 10 của một nsố nhị phân -bit mất đến n*log10(2) + 1chữ số. 10/33là nhiều hơn một chút log10(2). +1 cho ký hiệu charvà +1 cho ký tự null. Các phân số khác có thể được sử dụng như 28/93.


Phương pháp 3: Nếu một người muốn sống ở rìa và tràn bộ đệm không phải là vấn đề đáng lo ngại, một giải pháp C99 đơn giản hoặc mới hơn sẽ xử lý tất cả 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));
}

Sản lượng mẫu

0
1
-1
12345
2147483646
2147483647
-2147483647
-2147483648

Này chux, bạn có biết có cách nào để phơi bày việc thực hiện nội bộ glibc không? sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/ory
Ciro Santilli 冠状 病

1
@CiroSantilli 心 心 Tôi không quen với việc thực hiện nội bộ glibc.
chux - Phục hồi Monica

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

Nếu bạn muốn xuất cấu trúc của mình thành một tệp, không cần phải chuyển đổi bất kỳ giá trị nào trước đó. Bạn chỉ có thể sử dụng đặc tả định dạng printf để chỉ ra cách xuất giá trị của bạn và sử dụng bất kỳ toán tử nào từ họ printf để xuất dữ liệu của bạn.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.