วิธีการแปลง int เป็นสตริงใน C?


225

คุณจะแปลงint(จำนวนเต็ม) เป็นสตริงได้อย่างไร ฉันพยายามสร้างฟังก์ชั่นที่แปลงข้อมูลของ a structเป็นสตริงเพื่อบันทึกลงในไฟล์


3
printfหรือหนึ่งในลูกพี่ลูกน้องของมันควรทำเคล็ดลับ
pmg

1
มีความเป็นไปได้ที่ซ้ำกันของฟังก์ชัน itoa ใน Linux อยู่ที่ไหน
Paul R

1
คุณอาจต้องการดูคำถามที่พบบ่อยนี้ในการทำให้เป็นอนุกรมและอาจเป็นคำถามต่อไปนี้ที่เกี่ยวข้องกับการทำให้เป็นอนุกรมใน C: (a) , (b) , (c)เพื่อให้บรรลุเจตนาที่แท้จริงของคุณ
moooeeeep

2
สัตว์เลี้ยงของฉันมักจะโกรธความหมายที่นี่ คุณไม่ต้องการแปลงอะไรเลย คุณต้องการที่จะได้รับสตริงที่มีฐาน (10?) intเป็นตัวแทนของค่าของ ใช่ฉันรู้. มันเป็นทางลัดที่พบบ่อยมาก แต่ก็ยังบั๊กฉันอยู่
dmckee --- ผู้ดูแลอดีตแมว

อาจมีการซ้ำซ้อนของการแปลง int เป็น string ใน c
nawfal

คำตอบ:


92

แก้ไข: ตามที่ระบุไว้ในความคิดเห็น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เพื่อส่งออกข้อมูลของคุณ


29
itoaไม่ได้มาตรฐาน - ดูเช่นstackoverflow.com/questions/190229/…
พอล R

1
@ พอลเอาล่ะฉันไม่รู้ ขอขอบคุณสำหรับการชี้แจง.
Christian Rau

1
@ PaulR ขอบคุณฉันไม่รู้เหมือนกัน!
Alexander Galkin

@SeanRamey สิ่งนี้ทำให้เกิดitoa()ปัญหาบัฟเฟอร์ล้นเช่นgets()เดียวกัน
chux - Reinstate Monica

itoa ไม่สามารถใช้ได้ใน C (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
Christian Rau

23
หรือคุณสามารถคำนวณได้ในเวลาคอมไพล์:#define ENOUGH ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
caf

4
@hauleth ยังคง +2 แทนที่จะเป็น +1 ถึงแม้จะมี ceil: ceil (log (100)) = 2.0 ไม่ใช่ 3 ดังนั้น +1 สำหรับค่าที่แน่นอนของ 10 และ +1 สำหรับการยกเลิกค่า null
ไม่ใช่เพียงแค่ 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-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);

หากใช้ได้กับสตริงรูปแบบทุกรูปแบบดังนั้นคุณสามารถแปลง float หรือ double เป็น string โดยใช้"%g"คุณสามารถแปลง int เป็น hex ได้โดยใช้"%x"และต่อไป


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

@ user1821961 ขอบคุณมันควรจะกล่าวว่าไฟล์ส่วนหัวเหล่านี้จะต้องรวม
byxor

ฉันรักอันนี้เป็นวิธีที่แข็งแกร่งที่สุดและ "ตามหนังสือ"
Motti Shneor

30

หลังจากดูรุ่นต่างๆของ itoa สำหรับ gcc รุ่นที่ยืดหยุ่นที่สุดที่ฉันพบว่าสามารถจัดการการแปลงเป็นเลขฐานสองทศนิยมและเลขฐานสิบหกทั้งบวกและลบเป็นรุ่นที่สี่ที่พบที่http://www.strudel.org .uk / itoa / ในขณะที่sprintf/ snprintfมีข้อได้เปรียบพวกเขาจะไม่จัดการกับจำนวนลบสำหรับสิ่งอื่นใดนอกจากการแปลงทศนิยม เนื่องจากลิงค์ด้านบนเป็นแบบออฟไลน์หรือไม่ทำงานอีกต่อไปเราจึงรวมรุ่นที่ 4 ไว้ด้านล่าง:

/**
 * 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
นอกจากนี้ยังเร็วกว่า sprintf มาก อาจมีความสำคัญเมื่อทิ้งไฟล์ขนาดใหญ่
Eugene Ryabtsev

@Eugene Ryabtsev C ไม่ได้ระบุระดับประสิทธิภาพsprintf()และ "สิ่งนี้เร็วกว่า sprintf มาก" อาจเป็นจริงในคอมไพเลอร์ของคุณ แต่ไม่ได้ถือไว้เสมอ คอมไพเลอร์อาจพิจารณาsprintf(str, "%d", 42);และปรับให้เหมาะitoa()กับฟังก์ชั่นที่ได้รับการปรับให้ดีที่สุด- เร็วกว่าผู้ใช้รายนี้อย่างแน่นอน รหัส.
chux - Reinstate Monica

3
@ chux ใช่คอมไพเลอร์สามารถปรับให้เหมาะสมsprintf(str, "%d", 42);เมื่อผนวกสอง const const แต่นั่นคือทฤษฎี ในทางปฏิบัติผู้คนจะไม่วิ่งตามกันและ itoa ด้านบนนั้นเกือบจะได้รับการปรับให้เหมาะสมที่สุดเท่าที่จะทำได้ อย่างน้อยคุณก็มั่นใจได้ 100% ว่าคุณจะไม่ได้รับคำสั่งให้ลดขนาดของ sprintf ทั่วไป มันจะเป็นการดีถ้าได้เห็นตัวอย่างที่คุณมีอยู่ในใจด้วยเวอร์ชันคอมไพเลอร์และการตั้งค่า
Eugene Ryabtsev

1
@ chux หากมันจบลงด้วยการโทรมันไม่ได้อธิบายอะไรมากมายเว้นแต่เราจะเปรียบเทียบรูทีนที่เรียกไปยังรูทีนด้านบน (ตลอดจนถึงไม่มีการโทรอีก; ในแอสเซมบลีหากคุณต้องการ) หากคุณทำมันเป็นคำตอบกับเวอร์ชั่นคอมไพเลอร์และการตั้งค่าฉันจะโหวตว่ามีประโยชน์
Eugene Ryabtsev

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
ใช้งานได้กับค่าคงที่เท่านั้นไม่ใช่ตัวแปร
Nakedible

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จำเป็นในการเข้ารหัสชนิดจำนวนเต็มที่ลงนามแล้วบางส่วนเป็นสตริงที่ประกอบด้วยเครื่องหมายลบตัวเลือกตัวเลขและอักขระ null

CHAR_BIT*sizeof(int_type)-1จำนวนบิตที่ไม่เข้าสู่ระบบจำนวนเต็มลงนามคือไม่เกิน การแสดงพื้นฐาน -10 ของnเลขฐานสอง -bit นั้นใช้n*log10(2) + 1ตัวเลขได้สูงสุด เป็นมากกว่าเล็กน้อย10/33 log10(2)+1 สำหรับเครื่องหมายcharและ +1 สำหรับอักขระ null เศษส่วนอื่น ๆ สามารถใช้ได้เช่น 28/93


วิธีที่ 3: ถ้าใครต้องการที่จะมีชีวิตอยู่บนขอบและ Buffer overflow ไม่กังวลเป็น 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 - Reinstate 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

หากคุณต้องการส่งออกโครงสร้างของคุณไปยังไฟล์ไม่จำเป็นต้องแปลงค่าใด ๆ ไว้ล่วงหน้า คุณสามารถใช้ข้อกำหนดรูปแบบการพิมพ์เพื่อระบุวิธีการส่งออกค่าของคุณและใช้ตัวดำเนินการใด ๆ จากตระกูล printf เพื่อส่งออกข้อมูลของคุณ


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.