必要なメモリを自動的に割り当てる関数のsprintf()のような実装を探しています。だから言いたい
char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s Nice %05d string", a, b, c, d );
my_strは、このsprintf()の結果を保持する割り当てられたメモリのアドレスを取得します。
別のフォーラムで、これは次のように解決できると読みました:
#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;
}
ただし、これにより、NULLポインタを指定したsprintf()が呼び出されるとすぐにsegfaultが発生します。
アイデア、解決策、ヒントはありますか?パブリックドメインに配置されたsprintf()のようなパーサーの小さな実装はすでに十分であり、それから私はそれを自分で行うことができます。
どうもありがとう!
これが元の答え Stack Overflowから です。他の人が述べたように、snprintf
ではなくsprintf
が必要です。 snprintf
の2番目の引数がzero
であることを確認してください。これにより、snprintf
が最初の引数であるNULL
文字列に書き込むことができなくなります。
2番目の引数は、出力バッファーに書き込むための十分なスペースがないことをsnprintf
に通知するために必要です。十分なスペースが利用できない場合、snprintf
は、十分なスペースが利用可能であった場合に書き込まれるバイト数を返します。
ここのリンクからコードを再現しています...
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とBSDには asprintf とvasprintfがあり、それを行うために設計されています。メモリの割り当て方法がわかり、メモリ割り当てエラーが発生するとnullが返されます。
asprintfは文字列の割り当てに関して正しいことを行います-最初にサイズを測定し、次にmallocで割り当てようとします。それが失敗すると、nullを返します。 mallocの使用を妨げる独自のメモリ割り当てシステムがない限り、asprintfはジョブに最適なツールです。
コードは次のようになります。
#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;
}
GNU/BSDの拡張機能を使用できる場合は、質問はすでに回答されています。 asprintf()
(およびvasprintf()
を使用してラッパー関数を作成)し、実行することができます。
しかしsnprintf()
とvsnprintf()
は、マンページによればPOSIXによって義務付けられており、後者はasprintf()
と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;
}
いくつかのプリプロセッサのマジックを実行して、関数のバージョンを、それらをサポートしないシステムでのみ使用できます。
snprintf
-を使用すると、生成されるデータのサイズを簡単に測定できるため、スペースを割り当てることができます。fprintf
で一時ファイルに出力してサイズを取得し、メモリを割り当てることです、次にsprintfを使用します。 snprintf
は間違いなく推奨される方法です。GLib ライブラリーは _g_strdup_printf
_ 関数を提供します。GLibへのリンクがオプションの場合、この関数は正確に機能します。ドキュメントから:
標準のC
sprintf()
関数と似ていますが、必要な最大スペースを計算し、結果を保持するためのメモリを割り当てるため、より安全です。返された文字列は、不要になったときにg_free()
で解放する必要があります。
POSIX.1(別名IEEE 1003.1-2008)は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)は少なくともLinuxとmacOSで利用可能で、数年前から存在しています。 open_memstream(3)の逆はfmemopen(3)であり、これによりバッファの内容を読み取り可能にします。
単一のsprintf(3)が必要な場合は、広く実装されているが非標準のasprintf(3)が必要な場合があります。
/* 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);
}