3

I wish to insert some characters into a string in C:

Example: char string[100] = "20120910T090000";

I want to make it something like "2012-09-10-T-0900-00"

My code so far:

void append(char subject[],char insert[], int pos) {
    char buf[100]; 

    strncpy(buf, subject, pos); 

    int len = strlen(buf);

    strcpy(buf+len, insert); 

    len += strlen(insert);  

    strcpy(buf+len, subject+pos); 
    strcpy(subject, buf);   
}

When I call this the first time I get: 2012-0910T090000

However when I call it a second time I get: 2012-0910T090000-10T090000

Any help is appreciated

Jordan Arsenault
  • 7,100
  • 8
  • 53
  • 96
John Li
  • 33
  • 1
  • 4
  • A `char string[20]` is too small to hold the null terminator as well as `2012-09-10-T-0900-00` (which is 20 characters excluding the null). – Jonathan Leffler Oct 05 '12 at 04:11
  • Note that since you don't show us how you invoke the function, we can't tell what's going wrong. Note too that `strncpy()` does not guarantee null termination of the target string, so `strlen(buf)` is not guaranteed to be given a valid string. Since you know the lengths, it will be more efficient if you use `memcpy()` or `memmove()`. It is not strictly necessary to copy the data into the temporary array and then back to the original. – Jonathan Leffler Oct 05 '12 at 04:29

2 Answers2

2

Here's some working code, which gives me the output:

String: <<20120910T090000>>
String: <<2012-0910T090000>>
String: <<2012-09-10T090000>>
String: <<2012-09-10-T090000>>
String: <<2012-09-10-T-090000>>
String: <<2012-09-10-T-0900-00>>

It uses memmove() because it is guaranteed that the strings being copied around overlap.

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

static void insert(char *str, size_t len, char c, size_t pos)
{
    memmove(&str[pos+1], &str[pos], len - pos + 1);
    str[pos] = c;
}

int main(void)
{
    char string[25] = "20120910T090000";
    // I want to make it something like "2012-09-10-T-0900-00"
    char inschr[] = "-----";
    int  inspos[] = { 4, 7, 10, 12, 17 };
    enum { NUMCHR = sizeof(inschr) / sizeof(inschr[0]) };
    enum { NUMPOS = sizeof(inspos) / sizeof(inspos[0]) };
    assert(NUMCHR == NUMPOS + 1);
    size_t length = strlen(string);

    printf("String: <<%s>>\n", string);
    for (int i = 0; i < NUMPOS; i++)
    {
        insert(string, length, inschr[i], inspos[i]);
        length++;
        printf("String: <<%s>>\n", string);
    }
    return(0);
}

Of course, I'm assuming C99 support with the for loop notation. Note, too, that in classic C style, the code does not take the size of the target string, so it does not ensure that there is no buffer overflow. It wouldn't be all that hard to add the parameter and do the check; the problem area is how to indicate that the function failed. You could use a different interface to the function, taking arbitrary length strings to be inserted at arbitrary positions; it isn't significantly harder...

String: <<20120910T090000>>
String: <<2012-0910T090000>>
String: <<2012-09-10T090000>>
String: <<2012-09-10-T090000>>
String: <<2012-09-10-T-090000>>
String: <<2012-09-10-T-0900-00>>

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

static int insert(char *str, size_t max, size_t len, char *ins, size_t pos)
{
    assert(str[len] == '\0');
    assert(len < max);
    size_t inslen = strlen(ins);
    if (len + inslen + 1 >= max)
        return -1;
    memmove(&str[pos+inslen], &str[pos], len - pos + inslen);
    memmove(&str[pos], ins, inslen);
    return len + inslen;
}

int main(void)
{
    char string[25] = "20120910T090000";
    // I want to make it something like "2012-09-10-T-0900-00"
    char *insstr[] = { "-", "-", "-", "-", "-" };
    int   inspos[] = {   4,   7,  10,  12,  17 };
    enum { NUMSTR = sizeof(insstr) / sizeof(insstr[0]) };
    enum { NUMPOS = sizeof(inspos) / sizeof(inspos[0]) };
    size_t length = strlen(string);
    assert(NUMSTR == NUMPOS);

    printf("String: <<%s>>\n", string);
    for (int i = 0; i < NUMPOS; i++)
    {
        int newlen = insert(string, sizeof(string), length, insstr[i], inspos[i]);
        if (newlen < 0)
        {
            printf("Oops! failed to insert [%s] into [%s]\n", insstr[i], string);
            break;
        }
        length = newlen;
        printf("String: <<%s>>\n", string);
    }
    return(0);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • It's [C, not C++](http://stackoverflow.com/questions/1674032/static-const-vs-define-in-c/). I can use `enum` where a compile time constant is needed. A `const int` is not a compile time constant. In this code, it doesn't matter. In other code, it might. C99 blurs the distinction a bit; a `const int` dimension on an array makes it into VLA, even if the size is fixed. – Jonathan Leffler Oct 14 '12 at 11:10
1

You could do something like this:

  char src[100] = "20120910T090000";
  char mask[] = "NNNN-NN-NN-C-NN-NN-NN";
  char dest[100] = {0};

  for (size_t s = 0,m = 0, d = 0; s < strlen(src) && m < strlen(mask); ++m)
  {
    switch (mask[m])
    {
    case 'N':
      assert(isdigit(src[s]));
      dest[d++] = src[s++];
      break;
    case 'C':
      assert(isalpha(src[s]));
      dest[d++] = src[s++];
      break;
    default:
      dest[d++] = mask[m];
      break;
    }
  }
AndersK
  • 35,813
  • 6
  • 60
  • 86