Hello All,
From language like Python I could do the following;
foo = ["New York", "New Jersey","New London"]
foo.join(" ")
That would join then.
In C, how am I going to reproduce the same?
Sometimes it can be nice to track the length of a string, or a buffer. I
was a bit bored, so here's something you might or might not enjoy. With
any luck, it doesn't have any off-by-one errors.
Also available at the following link, which has some nice colours:
http://codepad.org/CKfqOSG2
/* For printf() */
#include <stdio.h>
/* For malloc(), free(), EXIT_SUCCESS, EXIT_FAILURE */
#include <stdlib.h>
/* For memcpy() */
#include <string.h>
struct string {
/*
* Count of characters in the string,
* excluding any null terminator, in bytes
*/
size_t len;
/* Size of the buffer itself, in bytes */
size_t max_len;
/* Points to the buffer */
char * buf;
/* For allocation convenience, the buffer can co-incide with this */
char appended_buf[1];
};
/* You can use this macro for [wordy] convenience, as an initializer */
#define STRING_INIT_FROM_STRING_LITERAL(string) \
{sizeof string - 1, sizeof string, string, 0}
/* Join non-empty strings with a specified, non-empty delimiter string */
int join_strings_with_delim(
struct string * output_str,
const struct string * const * input_strings,
const unsigned int input_string_count,
const struct string * delim,
int allocation
) {
int i;
size_t output_size;
char * buf;
/* Check output string */
if (!output_str)
return EXIT_FAILURE;
/* Check delimiter string */
if (!delim || !delim->buf || !delim->len)
return EXIT_FAILURE;
/* Check the strings to be joined */
if (!input_string_count || input_string_count < 2 || !input_strings)
return EXIT_FAILURE;
for (i = 0, output_size = 0; i < input_string_count; ++i) {
/* Check each input string */
if (!input_strings
|| !input_strings->buf ||
!input_strings->len)
return EXIT_FAILURE;
/* Compute the output size */
output_size += input_strings->len;
}
/* Include the delimiter string size in the computation */
output_size += delim->len * (input_string_count - 1);
/* Include the null terminator */
++output_size;
/* Should we allocate? */
if (allocation) {
buf = malloc(output_size);
if (!buf)
return EXIT_FAILURE;
output_str->buf = buf;
output_str->max_len = output_size;
/* Exclude the null terminator from the string length */
output_str->len = output_size - 1;
} else {
/* We don't allocate, so the caller ought to have */
if (!output_str->buf || output_size > output_str->max_len)
return EXIT_FAILURE;
buf = output_str->buf;
}
/* Perform the join */
i = 0;
/* Skip the delimiter the first time around */
goto skip_delim;
do {
size_t slen;
slen = delim->len;
memcpy(buf, delim->buf, slen);
buf += slen;
skip_delim:
slen = input_strings->len;
memcpy(buf, input_strings->buf, slen);
buf += slen;
++i;
} while (i < input_string_count);
/* Terminate the string */
*buf = 0;
return EXIT_SUCCESS;
}
void free_string_buf(struct string * str) {
if (!str) {
/* Programmer error! */
return;
}
free(str->buf);
str->buf = NULL;
str->len = str->max_len = 0;
return;
}
int main(void) {
/* These strings don't change, so we use 'static const' */
static const struct string foo[] = {
STRING_INIT_FROM_STRING_LITERAL("New York"),
STRING_INIT_FROM_STRING_LITERAL("New Jersey"),
STRING_INIT_FROM_STRING_LITERAL("New London"),
};
/* An array of pointers to these strings. It oughtn't to change */
static const struct string * const foo_ptrs[] =
{foo, foo + 1, foo + 2};
/* An output string for the joined strings */
struct string foo_output;
/* Or without the macro, we could have used the more redundant: */
static const struct string bar[] = {
{sizeof "New York" - 1, sizeof "New York", "New York", 0},
{sizeof "New Jersey" - 1, sizeof "New Jersey", "New Jersey", 0},
{sizeof "New London" - 1, sizeof "New London", "New London", 0},
};
/* An array of pointers to these strings. It oughtn't to change */
static const struct string * const bar_ptrs[] =
{bar, bar + 1, bar + 2};
/* An output string for the joined strings */
struct string bar_output;
/* The delimiter string. It does not change */
static const struct string delim_str =
STRING_INIT_FROM_STRING_LITERAL(" ");
/* Track any errors */
int status;
/*
* Join the strings, with the specified delimiter,
* into the output buffer. The number of strings to process
* can be calculated with 'sizeof array / sizeof *array'.
* We happen to know that it's 3, but code can change over time
*/
status = join_strings_with_delim(
&foo_output,
foo_ptrs,
sizeof foo_ptrs / sizeof *foo_ptrs,
&delim_str,
1 /* Allocate the output buffer */
);
if (status == EXIT_FAILURE) {
printf("Allocating foo_output failed!\n");
return EXIT_FAILURE;
}
printf("foo_output: \"%s\"\n", foo_output.buf);
/* Free the output buffer */
free_string_buf(&foo_output);
/* Same for 'bar' */
status = join_strings_with_delim(
&bar_output,
bar_ptrs,
sizeof bar_ptrs / sizeof *bar_ptrs,
&delim_str,
1 /* Allocate the output buffer */
);
if (status == EXIT_FAILURE) {
printf("Allocating bar_output failed!\n");
return EXIT_FAILURE;
}
printf("bar_output: \"%s\"\n", bar_output.buf);
/* Free the output buffer */
free_string_buf(&bar_output);
return EXIT_SUCCESS;
}
Pretty sure it'll be sizeof(char *), not sizeof(char[2]).
Woah.
That I could be wrong about this for twenty years without noticing sort
of suggests that it's esoteric, but it's still pretty basic stuff. *sigh*