У меня есть это:
#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
нужны определенные периферийные устройства).
Но даже если это работает, действительно ли это самый эффективный способ сделать это?
char nameAll[17];
Зачем экономить? char nameAll[ 32 ]; sprintf( nameAll, "Prefix%d.png", i );
Одно утверждение... И не сбрасывайте буфер потом. Ненужно... Пишите работающий код и не беспокойтесь о производительности... Компьютеры больше не делают из электронных ламп...
Немного не по теме, но поведение char nameAll[17]; strcat(nameAll, pFilename);
не определено, потому что nameAll[] не очищено (т. е. не является действительной строкой, к которой вы можете применить strcat). Используйте strcpy
в первый раз или просто nameAll[0] = '\0'
@ Fe2O3 Я использовал 17 в качестве размера строки просто .. потому что. Без особой причины. Я не был уверен, что смогу сделать это с sprintf
, везде люди рекомендовали вместо этого snprintf
. Может в разных сценариях и я неправильно понял
Значение 17 заставляет читателя спросить: «Правильно ли это? Достаточно большой?» Использование 32, 100 или 128 не должно влиять на вашу программу, если она действительно не сжата для стека (в наши дни таких мало). Если данные явно не собираются переполнять буфер достаточного размера, sprintf()
должно быть хорошо. Да, используйте snprintf()
при работе с менее предсказуемыми данными...
эффективность времени? Это не должно быть реальной проблемой, генерация 100 имен файлов, независимо от используемого метода, не потребует значительного количества времени. Самый простой — использовать что-то вроде sprintf(nameAll,"Something%d.png",'i);
@ATSooking4things Вы можете полностью забыть о sprintf
, потому что snprintf
может делать все, что может, и даже больше. sprintf
имеет устаревший дизайн API, но люди все еще используют его, потому что он все еще присутствует во многих устаревших учебных ресурсах, включая некоторые советы на этом сайте.
@ user694733, так ты говоришь мне, что я должен делать snprintf(nameAll, sizeof(nameAll), "Something%d.png", i);
вместо sprintf(nameAll, "Something%d.png",i)
?
Вам нужно только обновить число + суффикс части строки.
@ATSooking4things Да.
@ATSooking4things: определенно предпочитаю snprintf
sprintf
.
Обычно гораздо проще собрать детали. Либо с помощью 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% преобразований включают только изменение последней цифры и не требуется обновление суффикса. Вы также можете напрямую увеличить цифровую строку, что удалит все деления.
fwrite_unlocked()
значительно быстрее, чем printf()
, но не то, что мы оптимизируем.
@chqrlie В любом случае у меня была большая часть кода из некоторого кода регистрации, который я написал, и «для развлечения» было единственной причиной, по которой я написал свой ответ для начала.
В таких случаях нет смысла собирать части из соображений производительности: составить имя файла с помощью 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 мс Хорошая работа! Я подумал, что ваше решение было интересным.
«только» 30 мс могут помочь или нет. Пока не уверен, но я работаю с несколько ограниченными требованиями. Сам function()
занимает много времени (по крайней мере, на 1 порядок выше, да). Это из библиотеки, с которой я все еще экспериментирую. Когда я писал этот вопрос, я не clock()
проверял его и думал, что это будет намного быстрее. Сначала я попытаюсь увеличить его производительность. Если этого недостаточно, я воспользуюсь вашей реализацией. Спасибо!
@ATSooking4things: не 30 мс, а 30 микросекунд! Эта оптимизация бесполезна в вашем случае и приведена только в качестве примера того, как сжать некоторые циклы за счет удобочитаемости и ремонтопригодности.
Ты прав. Я неправильно понял. Спасибо за наводку, и тем не менее спасибо за решение
возможно, самым быстрым было бы иметь строку «Something___.png» и sprintf число прямо туда, где находятся _ (либо с% 03d, либо с «% d.png», если вы не хотите конечные нули), тогда вы не вообще не нужно делать stringcopies