Skip to main content
added 7 characters in body; edited tags
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

I wrote this code as an answer to a question. But I'd like you to have a look at it. This post is basically a copy of the answer I posted.

The code does no error checking. It assumes that the output buffer exists and is big enough and that srcsrc, origorig and newnew are valid strings. The headers needed:

I wrote this code as an answer to a question. But I'd like you to have a look at it. This post is basically a copy of the answer I posted

The code does no error checking. It assumes that the output buffer exists and is big enough and that src, orig and new are valid strings. The headers needed:

I wrote this code as an answer to a question. But I'd like you to have a look at it. This post is basically a copy of the answer I posted.

The code does no error checking. It assumes that the output buffer exists and is big enough and that src, orig and new are valid strings. The headers needed:

Became Hot Network Question
Tweeted twitter.com/StackCodeReview/status/1412199655417561096
Source Link
klutt
  • 903
  • 5
  • 16

Replace n or all occurrences of substring in C

I wrote this code as an answer to a question. But I'd like you to have a look at it. This post is basically a copy of the answer I posted

The code does no error checking. It assumes that the output buffer exists and is big enough and that src, orig and new are valid strings. The headers needed:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>

The main replace function

// Replace the first occurrence of orig in src with new and write result to dest
// Return pointer to first character after the end of first match in src and
// NULL if no match
const char *replace(char *dest, const char *src,
            const char *orig, const char *new) {
    char *ptr = strstr(src, orig); // First match

    // If no match, we want dest to contain a copy of src
    if(!ptr) {
        strcpy(dest, src);
        return NULL;
    }

    const ptrdiff_t offset = ptr - src; // Calculate offset
    const int origlen = strlen(orig);
    
    strncpy(dest, src, offset); // Copy everything before match
    strcpy(&dest[offset], new); // Copy replacement
    strcpy(&dest[offset + strlen(new)], &src[offset + origlen]); // Copy rest

    return src + offset + origlen;
}

Then we just add a function to handle N occurrences

// Replace maximum n occurrences. Stops when no more matches.
// Returns number of replacements
size_t replaceN(char *dest, const char *src, const char *orig, 
                const char *new, size_t n) {
    size_t ret = 0;

    // Maybe an unnecessary optimization to avoid multiple calls in 
    // loop, but it also adds clarity
    const int newlen = strlen(new);
    const int origlen = strlen(orig);
    
    do {
        const char *ptr = replace(dest, src, orig, new); // Replace

        if(!ptr) return ret; // Quit if no more matches

        // Length of the part of src before first match
        const ptrdiff_t offset = ptr - src;
    
        src = ptr; // Move src past what we have already copied.
    
        ret++;

        dest += offset - origlen + newlen; // Advance pointer to dest to the end

    } while(n > ret);

    return ret;
}

And a simple wrapper for all occurrences. Note that it's safe to use SIZE_MAX here, because there will never be more occurrences than that.

// Replace all. Returns the number of replacements, because why not?
size_t replaceAll(char *dest, const char *src, const char *orig, const char *new) {
    return replaceN(dest, src, orig, new, SIZE_MAX); 
}

It's easy to write a wrapper for the allocation. We borrow some code from https://stackoverflow.com/q/9052490/6699433

size_t countOccurrences(const char *str, const char *substr) {
    size_t count = 0;

    while((str = strstr(str, substr)) != NULL) {
       count++;
       str++; // We're standing at the match, so we need to advance
    }

    return count;
}

Then some code to calculate size and allocate a buffer

// Allocate a buffer big enough to hold src with n replacements 
// of orig to new
char *allocateBuffer(const char *src, const char *orig, 
                     const char *new, size_t n) {
    return malloc(strlen(src) + 
                  n * (strlen(new) - strlen(orig)) +
                  1 // Remember the zero terminator
                 );
}

And the two final functions

// Allocates a buffer and replaces max n occurrences of orig with new and
// writes it to the allocated buffer.
// Returns the buffer and NULL if allocation failed
char *replaceNAndAllocate(const char *src, const char *orig, 
                          const char *new, size_t n) {
    const size_t count = countOccurrences(src, orig);

    n = n < count ? n : count; // Min of n and count

    char *buf = allocateBuffer(src, orig, new, n);

    if(!buf) return NULL;

    replaceN(buf, src, orig, new, n);

    return buf;
}

// Allocates a buffer and replaces all occurrences of orig with new and
// writes it to the allocated buffer.
// Returns the buffer and NULL if allocation failed
char *replaceAllAndAllocate(const char *src, const char *orig, const char *new) {
    return replaceNAndAllocate(src, orig, new, SIZE_MAX);
}

And finally, a simple main with a test with multiple occurrences and with original string being a substring of replacement string:

int main(void) {
    char src[] = "!!!asdf!!!asdf!!!asdf!!!asdf!!!";
    char orig[] = "asdf";
    char new[] = "asdfaaaaaaaaasdf";
    
    puts(replaceAllAndAllocate(src, orig, new));
}

No warnings with -Wall -Wextra -pedantic and the output is:

$ ./a.out 
!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!