Search code examples
cstringperformancestrcat

How to add a number to the middle of a string in a time-efficient way?


I have this:

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

int main(void){
    const char* pFilename = NULL;

    pFilename = "Something.png"

    functionX(pFilename, argX);
}

But then, I'd like to call that function inside a loop, with different filenames like "Something0.png", "Something1.png", etc

After a bit of digging, I came up with this:

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

int main(void){
    const char* pFilename = NULL;
    char buffer[4];
    char nameStd[] = "Something";
    char namePng[] = ".png";
    char nameAll[17];

    pFilename = "Something.png"

    for (i = 0; i < 100; i++) {
        snprintf(buffer, sizeof(buffer), "%d", i);
        strcat(nameAll, pFilename);
        strcat(nameAll, buffer);
        strcat(nameAll, namePng);
        functionX(nameAll, argX);
        memset(nameAll,0,strlen(nameAll));
    }
}

Well, I'm not sure this will work. And I cannot execute the code at the moment (as functionX needs specific peripherals). But even if it does work, is it really the most time-efficient way to do this?


Solution

  • It's usually much easier to assemble parts. Either via snprintf() as suggested above, or by building the string yourself. The prefix doesn't change and you only need to add the suffix when another digit is added. It's about 60% faster than snprintf() for 1e6 strings but probably not worth the complexity:

    #include <limits.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    
    // A constant expression of log10(INT_MAX) + 1
    #define str_uint32_t_len sizeof(uint32_t) * CHAR_BIT * 21306 / 70777 + 2
    
    uint32_t uint32_t_len(uint32_t i) {
        uint32_t n = !i;
        for(uint32_t j = i; j; j /= 10, n++);
        return n;
    }
    
    // s is not '\0' terminated
    char *uint32_t_to_str(uint32_t u, size_t u_len, char *s) {
        for(char *p = s + u_len; s <= --p; u /= 10)
            *p = '0' + u % 10;
        return s;
    }
    
    int main() {
        char prefix[] = "Something";
        char suffix[] = ".png";
    
        // builds the path template "Something\0.png"
        char path[sizeof prefix + str_uint32_t_len + sizeof suffix - 2] = {0};
        strcpy(path, prefix);
        strcpy(path + sizeof prefix, suffix);
    
        size_t u_len[] = {1, 1};
        for(uint32_t i = 0; i < 1000000; i++) {
            u_len[1] = uint32_t_len(i);
            uint32_t_to_str(i, u_len[1], path + sizeof prefix - 1);
            if(u_len[0] < u_len[1]) {
                u_len[0] = u_len[1];
                strcpy(path + sizeof prefix + u_len[1] - 1, suffix);
            }
            printf("%s\n", path);
        }
    }