Je recherche une implémentation de type sprintf () d'une fonction qui alloue automatiquement la mémoire requise. Je veux donc dire
char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s Nice %05d string", a, b, c, d );
et my_str récupère l'adresse d'une mémoire allouée qui contient le résultat de ce sprintf ().
Dans un autre forum, j'ai lu que cela peut être résolu comme ceci:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
int numbytes;
numbytes = sprintf( (char*)NULL, "%s %d %s!", a, c, b );
printf( "numbytes = %d", numbytes );
ret = (char*)malloc( ( numbytes + 1 ) * sizeof( char ) );
sprintf( ret, "%s %d %s!", a, c, b );
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
Mais cela entraîne immédiatement une erreur de segmentation lorsque le sprintf () avec le pointeur NULL est invoqué.
Vous avez donc une idée, une solution ou des conseils? Une petite implémentation d'un analyseur similaire à sprintf () qui est placé dans le domaine public serait déjà suffisante, alors je pourrais le faire moi-même.
Merci beaucoup!
Voici la réponse originale de Stack Overflow . Comme d'autres l'ont mentionné, vous avez besoin de snprintf
et non sprintf
. Assurez-vous que le deuxième argument de snprintf
est zero
. Cela empêchera snprintf
d'écrire dans la chaîne NULL
qui est le premier argument.
Le deuxième argument est nécessaire car il indique à snprintf
que suffisamment d'espace n'est pas disponible pour écrire dans le tampon de sortie. Lorsque suffisamment d'espace n'est pas disponible, snprintf
renvoie le nombre d'octets qu'il aurait écrit si suffisamment d'espace avait été disponible.
Reproduire le code de ce lien ici ...
char* get_error_message(char const *msg) {
size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno) + 1;
char *buffer = malloc(needed);
sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
return buffer;
}
GNU et BSD ont asprintf et vasprintf qui sont conçus pour faire exactement cela pour vous. Il découvrira comment allouer la mémoire pour vous et renverra null en cas d'erreur d'allocation de mémoire.
asprintf fait la bonne chose en ce qui concerne l'allocation de chaînes - il mesure d'abord la taille, puis il essaie d'allouer avec malloc. A défaut, il renvoie null. À moins que vous n'ayez votre propre système d'allocation de mémoire qui empêche l'utilisation de malloc, asprintf est le meilleur outil pour le travail.
Le code ressemblerait à:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
ret = asprintf( "%s %d %s!", a, c, b );
if (ret == NULL) {
fprintf(stderr, "Error in asprintf\n");
return 1;
}
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
Si vous pouvez vivre avec des extensions GNU/BSD, la question est déjà répondue. Vous pouvez utiliser asprintf()
(et vasprintf()
pour construire des fonctions wrapper) et c'est fait.
Mais snprintf()
et vsnprintf()
sont mandatés par POSIX, selon la page de manuel, et cette dernière peut être utilisée pour créer votre propre version simple de asprintf()
et vasprintf()
.
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap1;
size_t size;
char *buffer;
va_copy(ap1, ap);
size = vsnprintf(NULL, 0, fmt, ap1) + 1;
va_end(ap1);
buffer = calloc(1, size);
if (!buffer)
return -1;
*strp = buffer;
return vsnprintf(buffer, size, fmt, ap);
}
int
asprintf(char **strp, const char *fmt, ...)
{
int error;
va_list ap;
va_start(ap, fmt);
error = vasprintf(strp, fmt, ap);
va_end(ap);
return error;
}
Vous pouvez faire de la magie de préprocesseur et utiliser vos versions de fonctions uniquement sur les systèmes qui ne les prennent pas en charge.
snprintf
- cela donne un moyen facile de mesurer la taille des données qui seraient produites afin que vous puissiez allouer de l'espace.fprintf
pour obtenir la taille, allouez la mémoire , puis utilisez sprintf. snprintf
is certainement la méthode préférée cependant.La bibliothèque GLib fournit une fonction g_strdup_printf
qui fait exactement ce que vous voulez, si la liaison avec GLib est une option. De la documentation:
Similaire à la fonction C
sprintf()
standard mais plus sûre, car elle calcule l'espace maximum requis et alloue de la mémoire pour contenir le résultat. La chaîne retournée doit être libérée avecg_free()
lorsqu'elle n'est plus nécessaire.
POSIX.1 (alias IEEE 1003.1-2008) fournit open_memstream:
char *ptr;
size_t size;
FILE *f = open_memstream(&ptr, &size);
fprintf(f, "lots of stuff here\n");
fclose(f);
write(1, ptr, size); /* for example */
free(ptr);
open_memstream (3) est disponible sur au moins Linux et macOS et l'est depuis quelques années. L'inverse de open_memstream (3) est fmemopen (3) qui rend le contenu d'un tampon disponible pour la lecture.
Si vous voulez juste un seul sprintf (3), l'asprintf (3) largement implémenté mais non standard pourrait être ce que vous voulez.
/* casprintf print to allocated or reallocated string
char *aux = NULL;
casprintf(&aux,"first line\n");
casprintf(&aux,"seconde line\n");
printf(aux);
free(aux);
*/
int vcasprintf(char **strp,const char *fmt,va_list ap)
{
int ret;
char *strp1;
char *result;
if (*strp==NULL)
return vasprintf(strp,fmt,ap);
ret=vasprintf(&strp1,fmt,ap); // ret = strlen(strp1) or -1
if (ret == -1 ) return ret;
if (ret==0) {free(strp1);return strlen(*strp);}
size_t len = strlen(*strp);
*strp=realloc(*strp,len + ret +1);
memcpy((*strp)+len,strp1,ret+1);
free(strp1);
return(len+ret);
}
int casprintf(char **strp, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap,fmt);
ret =vcasprintf(strp,fmt,ap);
va_end(ap);
return(ret);
}