Как быстро добавить число в середину строки?

У меня есть это:

#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 нужны определенные периферийные устройства). Но даже если это работает, действительно ли это самый эффективный способ сделать это?

возможно, самым быстрым было бы иметь строку «Something___.png» и sprintf число прямо туда, где находятся _ (либо с% 03d, либо с «% d.png», если вы не хотите конечные нули), тогда вы не вообще не нужно делать stringcopies

Tommylee2k 22.02.2023 10:39
char nameAll[17]; Зачем экономить? char nameAll[ 32 ]; sprintf( nameAll, "Prefix%d.png", i ); Одно утверждение... И не сбрасывайте буфер потом. Ненужно... Пишите работающий код и не беспокойтесь о производительности... Компьютеры больше не делают из электронных ламп...
Fe2O3 22.02.2023 10:41

Немного не по теме, но поведение char nameAll[17]; strcat(nameAll, pFilename); не определено, потому что nameAll[] не очищено (т. е. не является действительной строкой, к которой вы можете применить strcat). Используйте strcpy в первый раз или просто nameAll[0] = '\0'

mmixLinus 22.02.2023 10:46

@ Fe2O3 Я использовал 17 в качестве размера строки просто .. потому что. Без особой причины. Я не был уверен, что смогу сделать это с sprintf, везде люди рекомендовали вместо этого snprintf. Может в разных сценариях и я неправильно понял

ATSlooking4things 22.02.2023 10:48

Значение 17 заставляет читателя спросить: «Правильно ли это? Достаточно большой?» Использование 32, 100 или 128 не должно влиять на вашу программу, если она действительно не сжата для стека (в наши дни таких мало). Если данные явно не собираются переполнять буфер достаточного размера, sprintf() должно быть хорошо. Да, используйте snprintf() при работе с менее предсказуемыми данными...

Fe2O3 22.02.2023 10:52

эффективность времени? Это не должно быть реальной проблемой, генерация 100 имен файлов, независимо от используемого метода, не потребует значительного количества времени. Самый простой — использовать что-то вроде sprintf(nameAll,"Something%d.png",'i);

Jean-Baptiste Yunès 22.02.2023 10:54

@ATSooking4things Вы можете полностью забыть о sprintf, потому что snprintf может делать все, что может, и даже больше. sprintf имеет устаревший дизайн API, но люди все еще используют его, потому что он все еще присутствует во многих устаревших учебных ресурсах, включая некоторые советы на этом сайте.

user694733 22.02.2023 11:04

@ user694733, так ты говоришь мне, что я должен делать snprintf(nameAll, sizeof(nameAll), "Something%d.png", i); вместо sprintf(nameAll, "Something%d.png",i)?

ATSlooking4things 22.02.2023 11:41

Вам нужно только обновить число + суффикс части строки.

Allan Wind 22.02.2023 11:45

@ATSooking4things Да.

user694733 22.02.2023 12:13

@ATSooking4things: определенно предпочитаю snprintfsprintf.

chqrlie 22.02.2023 21:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обычно гораздо проще собрать детали. Либо с помощью snprintf(), как было предложено выше, либо самостоятельно создав строку. Префикс не меняется, и вам нужно добавить суффикс только при добавлении другой цифры. Это примерно на 60% быстрее, чем snprintf() для строк 1e6, но, вероятно, не стоит такой сложности:

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

Чтобы сократить еще несколько циклов, вы можете исследовать вычисление частичных обновлений преобразованной строки цифр: 90% преобразований включают только изменение последней цифры и не требуется обновление суффикса. Вы также можете напрямую увеличить цифровую строку, что удалит все деления.

chqrlie 22.02.2023 20:50
fwrite_unlocked() значительно быстрее, чем printf(), но не то, что мы оптимизируем.
Allan Wind 22.02.2023 21:39

@chqrlie В любом случае у меня была большая часть кода из некоторого кода регистрации, который я написал, и «для развлечения» было единственной причиной, по которой я написал свой ответ для начала.

Allan Wind 23.02.2023 01:03

В таких случаях нет смысла собирать части из соображений производительности: составить имя файла с помощью 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

Имейте в виду, что это заметное улучшение производительности (в 27 раз быстрее) в большинстве случаев не оправдывает такой умный код: для вашего примера (100 итераций) разница составляет всего 30 микросекунд, 0,00003 секунды, едва измеримая.

Разница больше, чем я ожидал (на моей машине): snprintf: 62,290 мс s_update: 4,695 мс uint32_t_to_str: 28,212 мс Хорошая работа! Я подумал, что ваше решение было интересным.

Allan Wind 23.02.2023 01:09

«только» 30 мс могут помочь или нет. Пока не уверен, но я работаю с несколько ограниченными требованиями. Сам function() занимает много времени (по крайней мере, на 1 порядок выше, да). Это из библиотеки, с которой я все еще экспериментирую. Когда я писал этот вопрос, я не clock() проверял его и думал, что это будет намного быстрее. Сначала я попытаюсь увеличить его производительность. Если этого недостаточно, я воспользуюсь вашей реализацией. Спасибо!

ATSlooking4things 23.02.2023 09:38

@ATSooking4things: не 30 мс, а 30 микросекунд! Эта оптимизация бесполезна в вашем случае и приведена только в качестве примера того, как сжать некоторые циклы за счет удобочитаемости и ремонтопригодности.

chqrlie 23.02.2023 09:45

Ты прав. Я неправильно понял. Спасибо за наводку, и тем не менее спасибо за решение

ATSlooking4things 23.02.2023 11:05

Другие вопросы по теме