如何以省时的方式将数字添加到字符串的中间?

问题描述 投票:0回答:2

我有这个:

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

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

    pFilename = "Something.png"

    functionX(pFilename, argX);
}

但是,我想在循环中调用该函数,使用不同的文件名,如“Something0.png”、“Something1.png”等

经过一番挖掘,我想到了这个:

#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));
    }
}

好吧,我不确定这是否有效。而且我现在无法执行代码(因为

functionX
需要特定的外围设备)。 但即使它确实有效,它真的是最省时的方法吗?

c string performance strcat
2个回答
2
投票

组装零件通常要容易得多。可以按照上面的建议通过 snprintf() ,也可以自己构建字符串。前缀不会改变,您只需要在添加另一个数字时添加后缀。对于 1e6 字符串,它比

snprintf()
快大约 60%,但可能不值得这么复杂:

#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);
    }
}

0
投票

在这种情况下,出于性能原因组装零件是没有意义的:用

snprintf
组成文件名简单高效,而且比
fopen()
便宜得多。

这是一个简化版本:

#include <stdio.h>

int functionX(const char *filename, ...) {
    //printf("%s\n", filename);
}

int main(void) {
    int argX = 1;
    for (int i = 0; i < 100; i++) {
        char filename[32];
        snprintf(filename, sizeof filename, "Something%d.png", i);
        functionX(filename, argX);
    }
    return 0;
}

上面的代码是推荐的方法,对于大多数用途来说足够快。其余答案仅用于教育目的。


如果你绝对想压缩开销,你可以尝试直接更新字符串:

这里有一个带有基准的例子:

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

void functionX(const char *filename, ...) {
    //printf("%s\n", filename);
}

int s_update(char *s, int len) {
    for (char *p = s + len - 1;;) {
        if (++*p <= '9')
            return len;
        *p = '0';
        if (p == s) {
            memmove(s + 1, s, strlen(s) + 1);
            *s = '1';
            return len + 1;
        }
    }
}

int main(void) {
    char filename[32];
    int argX = 1;
    int repeat = 1000000;
    clock_t c0 = clock();
    for (int i = 0; i < repeat; i++) {
        snprintf(filename, sizeof filename, "Something%d.png", i);
        functionX(filename, argX);
    }
    clock_t c1 = clock();
    strcpy(filename, "Something0.png");
    char *p = strchr(filename, '0');
    int len = 1;
    for (int i = 0; i < repeat; i++) {
        functionX(filename, argX);
        len = s_update(p, len);
    }
    clock_t c2 = clock();

    printf("snprintf: %.3fms\n", (c1 - c0) * 1000.0 / CLOCKS_PER_SEC);
    printf("s_update: %.3fms\n", (c2 - c1) * 1000.0 / CLOCKS_PER_SEC);
    return 0;
}

输出:

snprintf: 81.443ms
s_update: 3.085ms

请注意,这种显着的性能改进(27x 更快)在大多数情况下并不能证明这种聪明的代码是正确的:对于您的示例(100 次迭代),差异仅为 30 微秒,0,00003 秒,几乎无法测量。

© www.soinside.com 2019 - 2024. All rights reserved.