如何在C语言中将整数转换为字符串?

377

如何将int(整数)转换为字符串?

我正在尝试编写一个函数,将struct的数据转换为字符串以便保存到文件中。


4
printf 或其类似函数应该能解决问题。 - pmg
1
可能是Linux中itoa函数在哪里?的重复问题。 - Paul R
1
您可能还想查看有关序列化的常见问题解答,以及与C语言中序列化相关的以下问题:(a)(b)(c),以实现您的实际意图。 - moooeeeep
4
这是我通常关注的语义问题。你不想进行转换;你想获取一个字符串,其中包含int值的(十进制?)表示。是的,我知道。这是一个非常常见的快捷方式,但它仍然让我感到烦恼。 - dmckee --- ex-moderator kitten
可能是在C中将int转换为字符串的重复问题。 - nawfal
1
你可以解除当前被采纳的答案并接受得票最高的答案吗? itoa 是非标准的,因此 snprintf 更具可移植性。 - S.S. Anne
11个回答

384
你可以使用sprintf来实现,或者如果你有的话,可以使用snprintf:
char str[ENOUGH];
sprintf(str, "%d", 42);

可以使用以下方法计算 str 中字符数(包括终止字符):

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

16
为确保ENOUGH足够,我们可以通过malloc(sizeof(char)*(int)log10(num))来实现。 - Hauleth
4
甚至可以加上+2,考虑到(int)log10(42)1 - Christian Rau
36
你可以在编译时计算它:#define ENOUGH ((CHAR_BIT * sizeof(int) - 1) / 3 + 2) - caf
5
@hauleth仍然是+2而不是+1,即使使用了ceil:ceil(log(100)) = 2.0,而不是3。因此,在精确的10的幂方和终止空值上加上+1,再加上另一个+1。 - not-just-yeti
57
请按如下方式进行:使用int length = snprintf(NULL, 0,"%d",42);获取长度,然后为字符串分配length+1个字符,不考虑减号和地区:千位分隔符和分组。 - user2622016
显示剩余7条评论

164

正如评论中指出的那样,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系列 的任何运算符来输出数据。


59
itoa 函数不是标准函数 - 可以查看例如 https://dev59.com/RHVC5IYBdhLWcg3wxEJ1。 - Paul R
1
itoa在C语言中不可用(至少在C99标准下),但在C++中更为常见。 - Motti Shneor
如何在不使用sprintfitoa的情况下获得此内容?可能需要编写自定义标准库来实现。 - S.S. Anne
4
你为什么把参数传递为10? - Rasathurai Karan
添加 #include <stdlib.h> - Danijel
显示剩余2条评论

155
答案:

简单回答:

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

首先,您需要找到足够的大小。如果您使用NULL, 0作为第一个参数调用snprintf,它将告诉您长度:

长一些的是:首先你需要找到足够的大小。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);

如果对于每个格式字符串都适用,因此您可以使用"%g"将浮点数或双精度转换为字符串,您可以使用"%x"将整数转换为十六进制等。


11
包含<stdio.h>和<stdlib.h>库。 - user1821961
我喜欢这种方式,因为它是最健壮和“按照规范”执行的方式。 - Motti Shneor
2
嘿,我知道这个答案很老了,但是为什么在malloc中要加上“+1”呢?是为了'\0'吗? - Flying_whale
2
是的,+1 是为了 '\0'。snprintf 返回不包括终止空字符的长度,但在第二个参数中它期望包括终止空字符的长度,因此长度为 length + 1。 - user2622016
1
@jinkwon 是的 https://dev59.com/OVsW5IYBdhLWcg3wZWfj#35036037 - Jin Kwon
显示剩余3条评论

45
在查看了各种适用于gcc的itoa版本之后,我发现最灵活的版本是第四个版本,它能够处理二进制、十进制和十六进制的转换,包括正数和负数。你可以在http://www.strudel.org.uk/itoa/找到该版本。虽然sprintf/snprintf有其优点,但它们只能处理十进制转换中的负数。由于上述链接要么已经下线,要么不再有效,我在下面附上了他们的第四个版本。
(重要提示:此代码采用GPLv3许可,因此如果您使用它编译某些内容,结果必须按照相同的条款发布。)
/**
 * 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';
  
    // Reverse the string
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }
    return result;
}

8
这比sprintf快得多,当转储大文件时可能非常重要。 - Eugene Ryabtsev
4
是的,编译器可以将sprintf(str, "%d", 42);优化为追加两个常量字符,但这只是理论。在实践中,人们不会使用sprintf来格式化常量整数,而上面的itoa几乎是最优化的。至少你可以百分之百地确信,你不会得到一个比通用的sprintf慢数个数量级的结果。如果您有任何反例,请告知使用的编译器版本和设置。 - Eugene Ryabtsev
1
@Eugene Ryabtsev char str[3]; sprintf(str, "%d", 42); --> MOV #121A,W4, MOV W4,AF4, MOV #2A,W0 = 2A, MOV #0,W4, CALL 105E 嵌入式编译器简单地将缓冲区和数字42传递给类似于itoa()的例程。 - chux - Reinstate Monica
1
@chux 如果最终以调用结束,除非我们将所调用的例程与上面的例程进行比较(一直到没有更多的调用为止;如果您喜欢,可以使用汇编语言),否则它并没有解释太多。如果您将其作为带有编译器版本和设置的答案,我将会将其评为有用,并为之点赞。 - Eugene Ryabtsev
3
最好提供一种方法来获取输出长度。我在这里实现了这个功能:https://dev59.com/kWUq5IYBdhLWcg3wV_Ei#52111436 + 编写了单元测试。 - Ciro Santilli OurBigBook.com
显示剩余9条评论

14

这里还有另一种方法。

#include <stdio.h>

#define atoa(x) #x

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

51
仅适用于常量,不适用于变量。 - Nakedible
如果我在那里放置一个宏,为什么它不起作用呢?难道它不是一个常量吗? - Edw590

9

如果您使用的是GCC编译器,您可以使用GNU扩展的asprintf函数。

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

一种合理的解释可能是:“sprintf函数可能很危险,因为它可能输出的字符数超过字符串s的分配大小...为了避免这个问题,可以使用'snprintf'或'asprintf'。” - Peter Mortensen

9

将任何内容转换为字符串应该是要么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;
}

方法二:如果缓冲区太小,它会返回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位二进制数的十进制表示最多需要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

嘿chux,你知不知道有没有什么方法可以暴露glibc的内部实现?https://sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/_itoa.c;h=3749ee97e320ceb62cbf76b6c93dd2beb38fe157;hb=3c03baca37fdcb52c3881e653ca392bba7a99c2b - Ciro Santilli OurBigBook.com
1
我不熟悉glibc的内部实现。 - chux - Reinstate Monica
应该是'0' + x % 10而不是'0' - x % 10(将二进制整数映射到十进制0和9之间的二进制ASCII编码)? - étale-cohomology
@étale-cohomology x%10 的值为[-9 ... 0],当x <= 0时,不是0到9。 '0' - x % 10 是正确的。使用int的负面避免了当i == INT_MINi = -i;的未定义行为,而此代码永远不会出现这种情况。 - chux - Reinstate Monica
啊,太酷了,谢谢。UB是什么? - étale-cohomology
1
@étale-cohomology UB --> 未定义行为。对INT_MIN取反是UB。 - chux - Reinstate Monica

6

sprintf 函数返回字节数,并添加一个空字符:

# include <stdio.h>
# include <string.h>

int main() {
    char buf[1024];
    int n = sprintf( buf, "%d", 2415);
    printf("%s %d\n", buf, n);
}

输出:

2415 4

关于“返回字节”的意思是不泄漏内存? - Peter Mortensen

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

0

改进了cnicutar的回答。

包括:

#include <math.h> // log10, floor
#include <stdio.h> // sprintf
#include <stdlib.h> // abs

创建您的数字:
int num = 123;

计算长度:

size_t len;
if (abs(num)) { // if not zero
    len = floor(log10(abs(num)) + 1); // taking 'round' numbers into account too
    if (num < 0) len++; // add minus sign
} else { // if zero
    len = 1;
}

然后你可以将字符串存储为本地变量:

char str[len];
sprintf(str, "%d", num);
// do stuff

或者作为指针:

char *str = malloc(len * sizeof(char));
sprintf(str, "%d", num);
// do stuff
free(str);

希望这能帮到你!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接