Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using C, convert a dynamically-allocated int array to a comma-separated string as cleanly as possible

Tags:

c

string

I’m much less experienced in C than I am in higher-level languages. At Cisco, we use C, and I sometimes run into something that would be easy to do in Java or Python, but very difficult to do in C. Now is one of those times.

I have a dynamically-allocated array of unsigned integers which I need to convert to a comma-separated string for logging. While the integers are not likely to be very large, they could conceptually be anywhere from 0 to 4,294,967,295 In Python, that’s one short line.

my_str = ','.join(my_list)

How elegantly can people do this in C? I came up with a way, but it’s gross. If anyone knows a nice way to do it, please enlighten me.

like image 582
Taras Mankovski Avatar asked Nov 17 '09 00:11

Taras Mankovski


2 Answers

Code now tested and builds under gcc.

In contrast to other answers, does not mandate C99.

The real problem here is not knowing the length of the string you're going to need. Getting a number is as easy as sprintf("%u", *num) using num to walk your array of ints, but how much space are you going to need? To avoid overrunning a buffer, you have to keep track of a lot of integers.

size_t join_integers(const unsigned int *num, size_t num_len, char *buf, size_t buf_len) {
    size_t i;
    unsigned int written = 0;

    for(i = 0; i < num_len; i++) {
        written += snprintf(buf + written, buf_len - written, (i != 0 ? ", %u" : "%u"),
            *(num + i));
        if(written == buf_len)
            break;
    }

    return written;
}

Notice, that I keep track of how much of the buffer I have used and use snprintf so I don't overrun the end. snprintf will tack on a \0, but since I'm using buf + written I will start at the \0 of the previous snprintf.

In use:

int main() {
    size_t foo;
    char buf[512];

    unsigned int numbers[] = { 10, 20, 30, 40, 1024 };

    foo = join_integers(numbers, 5, buf, 512);
    printf("returned %u\n", foo);
    printf("numbers: %s\n", buf);
}

Outputs:

returned 20
numbers: 10, 20, 30, 40, 1024

Forcing the limiting to kick in, instead of overrunning:

char buf[15];    
foo = join_integers(numbers, 5, buf, 14);
buf[14] = '\0';

Outputs, expectedly:

returned 14
numbers: 10, 20, 30, 4
like image 58
Jed Smith Avatar answered Oct 25 '22 15:10

Jed Smith


You can actually use a library like Glib, which contains functions like

gchar* g_strjoin (const gchar *separator, ...);

Joins a number of strings together to form one long string, with the optional separator inserted between each of them. The returned string should be freed with g_free().

(you still need to use g_snprintf(), possibly with g_printf_string_upper_bound() to ensure space)

like image 34
Suppressingfire Avatar answered Oct 25 '22 16:10

Suppressingfire