开发者

Modifying strings in C

I need to store the following strings in 3 char * variables in C:

[foo]
[foo]:[bar]
[foo]:[bar]:[tat]

Each individual string such as "foo" is received at one point of time via a char* pointer, the corresponding output string is produced immediately before receiving the second string, and the total number of individual strings is known at all times.

There can be an arbitrary number of strings.

I wrote the following code to print the strings:

for(i=0;i<num;i++)
    {  
        for(j=0;j<=i;j++)
            {
                printf("[%s]",str[j]);
                if(j!=i)
                {
                    printf(":");
                }
                else
                {
                    printf("\n");
                }
            }       
  开发者_C百科  }   

But I am unable to figure out a way of storing the desired output strings in variables, other than writing a messy case-by-case function using strcat() & some additional self-defined functions.

Is there a way to do this, that looks as clean as simply printing the strings?


This is a memory-management-and-string-handling-in-C-are-tedious problem. If I'm right in thinking that for n input strings, you want n output strings, each longer than the last, I'd do this:

char **results = malloc(num * sizeof(char*));
if (results == 0) return ENOMEM;
int lastlen = 0;
for (int i = 0; i < num; ++i) {
    char *newresult = malloc(lastlen + strlen(str[i]) + 4);
    if (newresult == 0) { /* even more tedious freeing code goes here */ }
    results[i] = newresult;
    char *ptr = newresult;
    if (i != 0) {
        ptr += sprintf(ptr, "%s:", results[i-1])
    }
    sprintf(ptr, "[%s]", str[i])
    lastlen = strlen(newresult);
}

Or something like that. Obviously there's a good opportunity to write more than one function here, to separate the generation of these strings from the storing of them in arrays. There's also the usual argument how we can do better in the risk-of-buffer-overflow stakes than sprintf + mental arithmetic.

Edit: stole the use of sprintf from Oskar.

Another edit: example with asprintf, which is available on GNU and BSD. This has the tedious freeing code included, the success path is actually quite good. Returns a newly-allocated array on success, 0 on error. The caller is assumed to know num and therefore know how long the array will be: if that's no use then the array could be null-terminated.

char **results = malloc(num * sizeof(char*));
if (results != 0) {
    const char *last = "";
    const char *sep = "";
    for (int i = 0; i < num; ++i) {
        if (asprintf(results + i, "%s%s[%s]", last, sep, str[i]) == -1) break;
        last = str[i];
        sep = ":";
    }
    if (i == num) return results; // success!
    // asprintf failed, so the value of results[i] is undefined
    while (i != 0) free(results[--i]);
    free(results); 
}
return 0;


You could use sprintf(). Using your code as an example:

for(i=0;i<num;i++)
{  
    char s[N];
    s[0] = '\0';
    for(j=0;j<=i;j++)
        {
            sprintf(s+strlen(s),"[%s]",str[j]);
            if(i!=j)
            {
                sprintf(s+strlen(s),":");
            }
            else
            {
                sprintf(s+strlen(s),"\n");
            }
        }      
    /* store 's' somewhere */
}   

Edit: I wouldn't recommend this solution for anything serious. But for a simple application where you just want to get things done it should do the job. Just have N large enough, or allocate it dynamically to be able to hold the largest string.


You have two distinct issues: ensuring you allocate enough space and also creating the string. There are lots of ways to balance the two, but using realloc() makes it pretty straightforward. Here's a solution that checks and, if needed, enlarges the buffer.

The trade-offs are basically code complexity vs performance, such as:

  • Calculating memory requirements and allocating once vs. doing it more simply (IMHO) in the loop.
  • Wasting memory by over-allocating vs wasting time by (re-)allocation frequently.
  • Using the simpler strcpy() vs. simpler usage with sprintf().

My version

char *joinCmds( char* str[], /* The strings to concatenate */
                int num )    /* the number of strings */
{
    int  len = 0, i, off = 0;
    char *out = NULL, sep;

    for(i=0; i<num; i++)
    {
        while (len < (off+4+strlen(str[i]))) {
            len += 16384;      /* Larger numbers wastes memory in for better performance */
            out = realloc( out, len );  /* Warning: some 'realloc()' take 3 parameters */
            /** Handle NULL (Out of Memory errors) */
        }
        sep  = (i < (num-1)) ? ':' : '\n';
        off += sprintf(&out[off], "[%s]%c", str[i], sep);
    }
    return out;
}


a solution with strcpy; with sprintf its also easy.

char out[1000],*p=out; /* out with enough size */
int i=0;
/* num > 0 */
for(; i<num && (p+=strlen(strcpy(p,"]:["))) ; p+=strlen(strcpy(p,str[i++])) ) ;
strcpy(p,"]");
puts(out+2);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜