1

How do you insert a string (source) into another string (dest) in C at a specific position, overwriting the destination string (instead of shifting the contents of the destination string at the insert position).

A function such as string_insert_and_replace abstracted below;

char str_dest[] = "abcdefg";
char str_source[] = "123";

//The 3rd argument is the position to insert at
string_insert_and_replace(str_dest, str_source, 3);

//str_dest should now be "abc123g"
6

4 Answers 4

0

Since a string is a pointer to its first character, if you know which index you want to write to, you can just do this:

void string_insert_and_replace(char *dest, char *src, size_t pos)
{
    while (*src)
    {
        *(dest + pos) = *src;
        dest++, src++;
    }
}

Assuming sufficient space in dest to hold the entirety of pos + strlen(src) + 1 (I think).

Sign up to request clarification or add additional context in comments.

1 Comment

I thought of something similar. Adding on to another answer on here, just modified to use while loops. The extra checks prevent writing to the dest string when it runs out of space. ``` void string_replace_pos(char dest[], char source[], char pos) { while((*dest != '\0') && (pos > 0)) { dest++; pos--; } while((*dest != '\0') && (*source != '\0')) { *dest = *source; dest++; source++; } } ```
0

Here's a version that works. I've included some unit tests as well:

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

char str_dest[] = "abcdefg";
char str_source[] = "123";

int dlen;

void
replace(char *dst,char *src,int pos)
{

    // find starting place in destination -- we must scan char-by-char in
    // case pos is larger than the destination string size
    for (;  (*dst != 0) && (pos > 0);  ++dst, --pos);

    // copy in source string until _it_ ends or we run out of room in the
    // destination
    for (;  (*dst != 0) && (*src != 0);  ++dst, ++src)
        *dst = *src;
}

void
test(int pos)
{
    char dst[dlen + 1];

    strcpy(dst,str_dest);
    replace(dst,str_source,pos);

    printf("POS: %d DST: '%s'\n",pos,dst);
}

int
main(void)
{

    dlen = strlen(str_dest);

    for (int pos = 0;  pos <= (dlen + 3);  ++pos)
        test(pos);

    return 0;
}

UPDATE:

I'm writing this code for a microcontroller, would the safety check affect performance?

Yes, the safety check requires that the destination string be scanned byte by byte to locate the end.

Note that there is no need to do strlen on the source string. See the string_replace_fast variation of your function below. It isn't any safer, but it is [approximately 2x] faster.

Also I think with this current code I am overwriting memory that doesn't belong to the dest string if the source string length plus the pos argument are greater than strlen(dest).

Yes, that is correct. You can see it in the test results below.

You may have to sacrifice a bit of speed. If this isn't a function that is called a lot, then go for the slower/safer version. You can only tell this with some benchmarking.

Here's a modified version of the test program that has benchmarking for your version and mine along with a variation or two:

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

char str_dest[] = "abcdefghijklimnopqrstuvwxyz";
char str_source[] = "123";
char *tmp;

int dlen;

typedef void (*func_p)(char *dst,char *src,int pos);

#define TIMEIT(_fnc,_pos) \
    timeit(_fnc,_pos,#_fnc)

char *
fixstr(const char *src)
{
    static char fix[1000];
    char *dst = fix;

    for (;  *src != 0;  ++src) {
        if ((*src >= 0x20) && (*src <= 0x7E))
            *dst++ = *src;
        else
            dst += sprintf(dst,"{?%2.2X?}",*src & 0xFF);
    }
    *dst = 0;

    return fix;
}

double
tvgetf(void)
{
    struct timespec ts;
    double sec;

    clock_gettime(CLOCK_REALTIME,&ts);
    sec = ts.tv_nsec;
    sec /= 1e9;
    sec += ts.tv_sec;

    return sec;
}

void
string_replace_pos(char *dest, char *source, int pos)
{
    int source_length = strlen(source);

    for (int i = 0; i < source_length; i++) {
        dest[pos + i] = source[i];
    }
}

void
string_replace_fast(char *dest, char *source, int pos)
{
    int chr;

    dest += pos;
    for (chr = *source++;  chr != 0;  chr = *source++, ++dest)
        *dest = chr;
}

void
replace(char *dst,char *src,int pos)
{

    // find starting place in destination -- we must scan char-by-char in
    // case pos is larger than the destination string size
    for (;  (*dst != 0) && (pos > 0);  ++dst, --pos);

    // copy in source string until _it_ ends or we run out of room in the
    // destination
    for (;  (*dst != 0) && (*src != 0);  ++dst, ++src)
        *dst = *src;
}

void
replace2(char *dst,char *src,int pos)
{
    int mlen = strlen(dst);

    // find starting place in destination -- we must scan char-by-char in
    // case pos is larger than the destination string size
    if (pos <= mlen)
        dst += pos;

    // copy in source string until _it_ ends or we run out of room in the
    // destination
    for (;  (*dst != 0) && (*src != 0);  ++dst, ++src)
        *dst = *src;
}

void
timeit(func_p func,int pos,const char *name)
{
    double tvbeg;

    strcpy(tmp,str_dest);
    tvbeg = tvgetf();

    for (int iter = 1;  iter <= 1000;  ++iter)
        func(tmp,str_source,pos);

    tvbeg = tvgetf() - tvbeg; \

    printf("POS: %d %.9f DST: '%s' (%s)\n",
        pos,tvbeg,fixstr(tmp),name);

    int clen = strlen(tmp);
    if (clen != dlen)
        printf("ERROR: length mismatch -- EXPECTED: %d ACTUAL: %d\n",dlen,clen);
}

void
test(int pos)
{

    printf("\n");
    TIMEIT(string_replace_pos,pos);
    TIMEIT(string_replace_fast,pos);
    TIMEIT(replace,pos);
    TIMEIT(replace2,pos);
}

int
main(void)
{

    dlen = strlen(str_dest);
    tmp = malloc(dlen + 100);

    for (int pos = -3;  pos <= (dlen + 3);  ++pos)
        test(pos);

    return 0;
}

Here is the output of this test program.

Note the ERROR lines for your versions. Also, note what happens if the insertion position is negative.

POS: -3 0.000008106 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: -3 0.000003815 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: -3 0.000007629 DST: '123defghijklimnopqrstuvwxyz' (replace)
POS: -3 0.000011683 DST: 'abcdefghijklimnopqrstuvwxyz' (replace2)

POS: -2 0.000007153 DST: '3bcdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: -2 0.000003815 DST: '3bcdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: -2 0.000007391 DST: '123defghijklimnopqrstuvwxyz' (replace)
POS: -2 0.000012159 DST: '3bcdefghijklimnopqrstuvwxyz' (replace2)

POS: -1 0.000007391 DST: '23cdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: -1 0.000003815 DST: '23cdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: -1 0.000007391 DST: '123defghijklimnopqrstuvwxyz' (replace)
POS: -1 0.000012159 DST: '23cdefghijklimnopqrstuvwxyz' (replace2)

POS: 0 0.000007391 DST: '123defghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 0 0.000004053 DST: '123defghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 0 0.000007391 DST: '123defghijklimnopqrstuvwxyz' (replace)
POS: 0 0.000012159 DST: '123defghijklimnopqrstuvwxyz' (replace2)

POS: 1 0.000007391 DST: 'a123efghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 1 0.000003815 DST: 'a123efghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 1 0.000008583 DST: 'a123efghijklimnopqrstuvwxyz' (replace)
POS: 1 0.000012159 DST: 'a123efghijklimnopqrstuvwxyz' (replace2)

POS: 2 0.000007153 DST: 'ab123fghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 2 0.000003815 DST: 'ab123fghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 2 0.000010014 DST: 'ab123fghijklimnopqrstuvwxyz' (replace)
POS: 2 0.000012159 DST: 'ab123fghijklimnopqrstuvwxyz' (replace2)

POS: 3 0.000007391 DST: 'abc123ghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 3 0.000003815 DST: 'abc123ghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 3 0.000011206 DST: 'abc123ghijklimnopqrstuvwxyz' (replace)
POS: 3 0.000015497 DST: 'abc123ghijklimnopqrstuvwxyz' (replace2)

POS: 4 0.000007629 DST: 'abcd123hijklimnopqrstuvwxyz' (string_replace_pos)
POS: 4 0.000004053 DST: 'abcd123hijklimnopqrstuvwxyz' (string_replace_fast)
POS: 4 0.000013351 DST: 'abcd123hijklimnopqrstuvwxyz' (replace)
POS: 4 0.000012636 DST: 'abcd123hijklimnopqrstuvwxyz' (replace2)

POS: 5 0.000007629 DST: 'abcde123ijklimnopqrstuvwxyz' (string_replace_pos)
POS: 5 0.000004053 DST: 'abcde123ijklimnopqrstuvwxyz' (string_replace_fast)
POS: 5 0.000014544 DST: 'abcde123ijklimnopqrstuvwxyz' (replace)
POS: 5 0.000012636 DST: 'abcde123ijklimnopqrstuvwxyz' (replace2)

POS: 6 0.000007391 DST: 'abcdef123jklimnopqrstuvwxyz' (string_replace_pos)
POS: 6 0.000004053 DST: 'abcdef123jklimnopqrstuvwxyz' (string_replace_fast)
POS: 6 0.000015736 DST: 'abcdef123jklimnopqrstuvwxyz' (replace)
POS: 6 0.000012636 DST: 'abcdef123jklimnopqrstuvwxyz' (replace2)

POS: 7 0.000007629 DST: 'abcdefg123klimnopqrstuvwxyz' (string_replace_pos)
POS: 7 0.000004053 DST: 'abcdefg123klimnopqrstuvwxyz' (string_replace_fast)
POS: 7 0.000018358 DST: 'abcdefg123klimnopqrstuvwxyz' (replace)
POS: 7 0.000012636 DST: 'abcdefg123klimnopqrstuvwxyz' (replace2)

POS: 8 0.000007629 DST: 'abcdefgh123limnopqrstuvwxyz' (string_replace_pos)
POS: 8 0.000004053 DST: 'abcdefgh123limnopqrstuvwxyz' (string_replace_fast)
POS: 8 0.000019550 DST: 'abcdefgh123limnopqrstuvwxyz' (replace)
POS: 8 0.000012636 DST: 'abcdefgh123limnopqrstuvwxyz' (replace2)

POS: 9 0.000007629 DST: 'abcdefghi123imnopqrstuvwxyz' (string_replace_pos)
POS: 9 0.000003815 DST: 'abcdefghi123imnopqrstuvwxyz' (string_replace_fast)
POS: 9 0.000020504 DST: 'abcdefghi123imnopqrstuvwxyz' (replace)
POS: 9 0.000012636 DST: 'abcdefghi123imnopqrstuvwxyz' (replace2)

POS: 10 0.000007629 DST: 'abcdefghij123mnopqrstuvwxyz' (string_replace_pos)
POS: 10 0.000003815 DST: 'abcdefghij123mnopqrstuvwxyz' (string_replace_fast)
POS: 10 0.000032425 DST: 'abcdefghij123mnopqrstuvwxyz' (replace)
POS: 10 0.000012159 DST: 'abcdefghij123mnopqrstuvwxyz' (replace2)

POS: 11 0.000007391 DST: 'abcdefghijk123nopqrstuvwxyz' (string_replace_pos)
POS: 11 0.000003815 DST: 'abcdefghijk123nopqrstuvwxyz' (string_replace_fast)
POS: 11 0.000021696 DST: 'abcdefghijk123nopqrstuvwxyz' (replace)
POS: 11 0.000012159 DST: 'abcdefghijk123nopqrstuvwxyz' (replace2)

POS: 12 0.000007391 DST: 'abcdefghijkl123opqrstuvwxyz' (string_replace_pos)
POS: 12 0.000003815 DST: 'abcdefghijkl123opqrstuvwxyz' (string_replace_fast)
POS: 12 0.000022888 DST: 'abcdefghijkl123opqrstuvwxyz' (replace)
POS: 12 0.000012159 DST: 'abcdefghijkl123opqrstuvwxyz' (replace2)

POS: 13 0.000007391 DST: 'abcdefghijkli123pqrstuvwxyz' (string_replace_pos)
POS: 13 0.000003815 DST: 'abcdefghijkli123pqrstuvwxyz' (string_replace_fast)
POS: 13 0.000023842 DST: 'abcdefghijkli123pqrstuvwxyz' (replace)
POS: 13 0.000012159 DST: 'abcdefghijkli123pqrstuvwxyz' (replace2)

POS: 14 0.000007153 DST: 'abcdefghijklim123qrstuvwxyz' (string_replace_pos)
POS: 14 0.000003815 DST: 'abcdefghijklim123qrstuvwxyz' (string_replace_fast)
POS: 14 0.000024796 DST: 'abcdefghijklim123qrstuvwxyz' (replace)
POS: 14 0.000015736 DST: 'abcdefghijklim123qrstuvwxyz' (replace2)

POS: 15 0.000007391 DST: 'abcdefghijklimn123rstuvwxyz' (string_replace_pos)
POS: 15 0.000003815 DST: 'abcdefghijklimn123rstuvwxyz' (string_replace_fast)
POS: 15 0.000025749 DST: 'abcdefghijklimn123rstuvwxyz' (replace)
POS: 15 0.000015497 DST: 'abcdefghijklimn123rstuvwxyz' (replace2)

POS: 16 0.000007153 DST: 'abcdefghijklimno123stuvwxyz' (string_replace_pos)
POS: 16 0.000003815 DST: 'abcdefghijklimno123stuvwxyz' (string_replace_fast)
POS: 16 0.000026941 DST: 'abcdefghijklimno123stuvwxyz' (replace)
POS: 16 0.000015497 DST: 'abcdefghijklimno123stuvwxyz' (replace2)

POS: 17 0.000007153 DST: 'abcdefghijklimnop123tuvwxyz' (string_replace_pos)
POS: 17 0.000003815 DST: 'abcdefghijklimnop123tuvwxyz' (string_replace_fast)
POS: 17 0.000027895 DST: 'abcdefghijklimnop123tuvwxyz' (replace)
POS: 17 0.000015497 DST: 'abcdefghijklimnop123tuvwxyz' (replace2)

POS: 18 0.000007153 DST: 'abcdefghijklimnopq123uvwxyz' (string_replace_pos)
POS: 18 0.000004053 DST: 'abcdefghijklimnopq123uvwxyz' (string_replace_fast)
POS: 18 0.000028849 DST: 'abcdefghijklimnopq123uvwxyz' (replace)
POS: 18 0.000015497 DST: 'abcdefghijklimnopq123uvwxyz' (replace2)

POS: 19 0.000007153 DST: 'abcdefghijklimnopqr123vwxyz' (string_replace_pos)
POS: 19 0.000003815 DST: 'abcdefghijklimnopqr123vwxyz' (string_replace_fast)
POS: 19 0.000029802 DST: 'abcdefghijklimnopqr123vwxyz' (replace)
POS: 19 0.000015497 DST: 'abcdefghijklimnopqr123vwxyz' (replace2)

POS: 20 0.000007391 DST: 'abcdefghijklimnopqrs123wxyz' (string_replace_pos)
POS: 20 0.000003815 DST: 'abcdefghijklimnopqrs123wxyz' (string_replace_fast)
POS: 20 0.000030994 DST: 'abcdefghijklimnopqrs123wxyz' (replace)
POS: 20 0.000015497 DST: 'abcdefghijklimnopqrs123wxyz' (replace2)

POS: 21 0.000007153 DST: 'abcdefghijklimnopqrst123xyz' (string_replace_pos)
POS: 21 0.000003815 DST: 'abcdefghijklimnopqrst123xyz' (string_replace_fast)
POS: 21 0.000031948 DST: 'abcdefghijklimnopqrst123xyz' (replace)
POS: 21 0.000015497 DST: 'abcdefghijklimnopqrst123xyz' (replace2)

POS: 22 0.000007153 DST: 'abcdefghijklimnopqrstu123yz' (string_replace_pos)
POS: 22 0.000003815 DST: 'abcdefghijklimnopqrstu123yz' (string_replace_fast)
POS: 22 0.000032902 DST: 'abcdefghijklimnopqrstu123yz' (replace)
POS: 22 0.000015497 DST: 'abcdefghijklimnopqrstu123yz' (replace2)

POS: 23 0.000007391 DST: 'abcdefghijklimnopqrstuv123z' (string_replace_pos)
POS: 23 0.000003815 DST: 'abcdefghijklimnopqrstuv123z' (string_replace_fast)
POS: 23 0.000034094 DST: 'abcdefghijklimnopqrstuv123z' (replace)
POS: 23 0.000015497 DST: 'abcdefghijklimnopqrstuv123z' (replace2)

POS: 24 0.000007153 DST: 'abcdefghijklimnopqrstuvw123' (string_replace_pos)
POS: 24 0.000003815 DST: 'abcdefghijklimnopqrstuvw123' (string_replace_fast)
POS: 24 0.000034571 DST: 'abcdefghijklimnopqrstuvw123' (replace)
POS: 24 0.000015497 DST: 'abcdefghijklimnopqrstuvw123' (replace2)

POS: 25 0.000007153 DST: 'abcdefghijklimnopqrstuvwx123' (string_replace_pos)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 28
POS: 25 0.000003815 DST: 'abcdefghijklimnopqrstuvwx123' (string_replace_fast)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 28
POS: 25 0.000034571 DST: 'abcdefghijklimnopqrstuvwx12' (replace)
POS: 25 0.000014305 DST: 'abcdefghijklimnopqrstuvwx12' (replace2)

POS: 26 0.000007153 DST: 'abcdefghijklimnopqrstuvwxy123' (string_replace_pos)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 29
POS: 26 0.000003815 DST: 'abcdefghijklimnopqrstuvwxy123' (string_replace_fast)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 29
POS: 26 0.000034809 DST: 'abcdefghijklimnopqrstuvwxy1' (replace)
POS: 26 0.000012636 DST: 'abcdefghijklimnopqrstuvwxy1' (replace2)

POS: 27 0.000007391 DST: 'abcdefghijklimnopqrstuvwxyz123' (string_replace_pos)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 30
POS: 27 0.000003815 DST: 'abcdefghijklimnopqrstuvwxyz123' (string_replace_fast)
ERROR: length mismatch -- EXPECTED: 27 ACTUAL: 30
POS: 27 0.000148058 DST: 'abcdefghijklimnopqrstuvwxyz' (replace)
POS: 27 0.000008821 DST: 'abcdefghijklimnopqrstuvwxyz' (replace2)

POS: 28 0.000007629 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 28 0.000003815 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 28 0.000039339 DST: 'abcdefghijklimnopqrstuvwxyz' (replace)
POS: 28 0.000012159 DST: '123defghijklimnopqrstuvwxyz' (replace2)

POS: 29 0.000007391 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 29 0.000003815 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 29 0.000035048 DST: 'abcdefghijklimnopqrstuvwxyz' (replace)
POS: 29 0.000011921 DST: '123defghijklimnopqrstuvwxyz' (replace2)

POS: 30 0.000007153 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_pos)
POS: 30 0.000003815 DST: 'abcdefghijklimnopqrstuvwxyz' (string_replace_fast)
POS: 30 0.000035048 DST: 'abcdefghijklimnopqrstuvwxyz' (replace)
POS: 30 0.000012159 DST: '123defghijklimnopqrstuvwxyz' (replace2)

2 Comments

Interesting implementation, this is what I did, the same thing but without any safety. I'm writing this code for a microcontroller, would the safety check affect performance? Also I think with this current code I am overwriting memory that doesn't belong to the dest string if the source string length plus the pos argument are greater than strlen(dest). void string_replace_pos(char dest[], char source[], char pos) { int source_length = strlen(source); for(int i = 0; i < source_length; i++) { dest[pos + i] = source[i]; } }
Dear Mr.Estey, I get previously your a lot of helps sir. Nowadays I need and I will need your helps as well. If you can glance at the problem on my simple program, I really appreciate you. I do its explanation as comment at the beginning of the file. My problem is merely "using" pipes as I explain in the comment. I am looking forward to hearing from you. The C file is snrkr.com/soner_aid.c Best Regards.
0

If one wants performance with long strings, consider memcpy, which is probably optimised to the architecture.

#include <string.h> /* memcpy strlen */
#include <stdio.h>  /* printf */
#include <assert.h>

static void string_insert_and_replace(char *str_dest,
    const char *str_source, const size_t offset) {
    const size_t len_dest = strlen(str_dest), len_source = strlen(str_source);
    size_t n = len_source;
    assert(str_dest && str_source);
    if(offset + len_source > len_dest) {
        if(offset >= len_dest) return;
        n = len_dest - offset;
    }
    memcpy(str_dest + offset, str_source, n);
}

int main(void) {
    char str_dest[] = "abcdefg";
    char str_source[] = "123";

    /* The 3rd argument is the position to insert at */
    string_insert_and_replace(str_dest, str_source, 5);

    /* str_dest should now be "abc123g" */
    printf("%s\n", str_dest);

    return 0;
}

Edit: In the former, if the source goes past the dest+offset, then strlen counts past the end of the copyable length for nothing. The following is modified to truncate the search.

#include <string.h> /* memchr memcpy strlen */
#include <stdio.h>  /* printf */
#include <assert.h>

static void string_insert_and_replace(char *str_dest,
    const char *str_source, const size_t offset) {
    const size_t len_dest = strlen(str_dest);
    size_t n;
    char *null_source;
    assert(str_dest && str_source);
    /* This is the maximum bytes it could copy without overflow. */
    if(offset >= len_dest) return;
    n = len_dest - offset;
    /* If source is shorter then the remaining dest. */
    if((null_source = memchr(str_source, '\0', n)))
        n = (size_t)(null_source - str_source);
    memcpy(str_dest + offset, str_source, n);
}

Comments

-1

Whenever you abstract any string functions, you should ALWAYS minimally supply the available size of any destination buffer, and, if possible, the max size of the source buffer to avoid issues with strings missing null terminators.

Which means something like

void string_insert_and_replace(char *dest, int dlen, const char *src, int slen, int off)
{
    ...
}

If you find yourself using strcpy and/or strlen instead of strncpy and strnlen you are doing it wrong. Ignore anyone's code that even suggests doing so.

2 Comments

Interesting comment about the strn* family of functions. You may find the discussion on this question interesting.
Indeed. In fact, I generally use xsnprintf() or snprintf() instead of strncpy() for exactly the reasons mentioned in that thread.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.