在C形式的“1,2,3,4,5”中创建一个字符串

时间:2014-05-14 10:40:34

标签: c string

我很难生成一个“1,2,3,4,5”形式的字符串以传递给命令行程序。

以下是我的尝试:

int N=100;
char list[200];
for (i=0; i<2*N; i+=2) {
    char tmp;
    sprintf(tmp,'%d', i);
    strcpy(list[i], tmp);
    strcpy(list[i+1], ',');
}

修改 我不认为这个问题是重复的,因为它更多的是将字符串附加到列表中并管理该内存,而不仅仅是在整数之间添加逗号。

2 个答案:

答案 0 :(得分:2)

以下代码可以满足您的需求。

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

char* CommaSeparatedListOfIntegers(const int N)
{
    if (N < 1)
        return NULL;
    char* result = malloc(1 + N*snprintf(NULL, 0, "%d,", N));
    char* p = result;
    for (int i = 1; i <= N; i++)
        p += sprintf(p, "%d,", i);
    *(p-1) = '\0';
    return result;
}

请注意,该函数返回一个堆分配的内存块,调用者负责清理它。

有些注意事项:

  • 转换为文本时,我们在每个数字的长度上加上粗略的上限。这意味着我们将过度分配内存块,但不会大量分配内存块。如果这对您来说是个问题,那么您可以编码更准确的长度。这将涉及从1循环到N并为每个值调用snprintf以确定所需的长度。
  • 请注意,我们最初在最终值之后写出一个逗号,但然后将其替换为null-terminator。

答案 1 :(得分:2)

让我们暂时忘记编写字符串并编写一个只将该列表打印到屏幕上的函数:

int range_print(int begin, int end, const char *sep)
{
    int len = 0;
    int i;

    for (i = begin; i < end; i++) {
        if (i > begin) {
            len += printf("%s", sep);
        }
        len += printf("%d", i);
    }

    return len;    
}

您可以这样称呼它:

range_print(1, 6, ", ");
printf("\n");

该函数不会写一个换行符,所以我们必须这样做。它会在第一个数字之后打印所有数字和自定义分隔符。分隔符可以是任何字符串,因此如果要使用斜杠或制表符分隔数字,此功能也可以使用。

该函数具有printf语义,因为它返回写入的字符数。 (这个值经常被忽略,但它可以派上用场,因为我们很快就会看到。)我们也使上限独占,以便打印(1,2,3,4,5)你将tp作为边界传递16

我们现在调整此函数以便它写入字符串。有几种方法可以做到这一点。让我们看看一种类似于snprintf的方式:它应该是一个预先分配的char缓冲区,最大长度,它应该返回写入的字符数,或者如果输出没有&#39; t fit,如果缓冲区足够大,那么写入的字符数就会很多。

int range(char *buf, int n, int begin, int end, const char *sep)
{
    int len = 0;
    int m, i;

    for (i = begin; i < end; i++) {
        m = snprintf(buf, n, "%s%d", 
            (i > begin) ? sep : "", i);
        len += m;
        buf += m;
        n -= m;
        if (n < 0) n = 0;
    }

    return len;    
}

这个函数很棘手,因为它必须跟踪写入的字符数和仍然可用的空闲缓冲区。它在缓冲区已满后继续打印,这在性能方面有点浪费,但调用缓冲区大小为零的snprintf是合法的,这样我们就可以保持语义的整洁。

您可以像这样调用此函数:

char buf[80];
range(buf, sizeof(buf), 1, 6, ", ");    
printf("%s\n", buf);

这意味着我们需要定义一个足够大的缓冲区。如果数字范围很大,则字符串将被截断。因此,我们可能需要一个为我们分配足够长的字符串的函数:

char *range_new(int begin, int end, const char *sep, int *plen)
{
    int len = (end - begin - 1) * strlen(sep) + 1;
    char *str;
    char *p;
    int i;

    for (i = begin; i < end; i++) {
        len += snprintf(NULL, 0, "%d", i);
    }

    str = malloc(len);
    if (str == NULL) return NULL;

    p = str;
    for (i = begin; i < end; i++) {
        if (i > begin) p += sprintf(p, "%s", sep);
        p += sprintf(p, "%d", i);
    }

    if (plen) *plen = len - 1;
    return str;
}

此函数需要两次传递:在第一次传递中,我们确定存储列表需要多少内存。接下来,我们分配并填充字符串。该函数返回分配的字符串,用户在使用后必须使用free。因为已经使用了返回值,所以我们会丢失有关字符串长度的信息。可以给出另一个参数,指向int的指针。如果不是NULL,则会存储长度。

可以像这样调用此函数。

char *r;
int len;

r = range_new(1, 6, ", ", &len);
printf("%s (%d)\n", r, len);
free(r);

请注意,通过两次调用我们的旧range函数可以实现相同的目的:

char *r;
int len;

len = range(NULL, 0, 1, 6, ", ");
r = malloc(len + 1);
range(p, len + 1, 1, 6, ", ");
printf("%s (%d)\n", r, len);
free(r);

所以,选择一个。对于短程,我建议使用带有固定大小缓冲区的简单range函数。