创建C格式的字符串(不打印它们)

pis*_*hio 91 c string

我有一个接受字符串的函数,即:

void log_out(char *);
Run Code Online (Sandbox Code Playgroud)

在调用它时,我需要动态创建一个格式化的字符串,如:

int i = 1;
log_out("some text %d", i);
Run Code Online (Sandbox Code Playgroud)

我如何在ANSI C中执行此操作?


只是,因为sprintf()返回一个int,这意味着我必须编写至少3个命令,如:

char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);
Run Code Online (Sandbox Code Playgroud)

有什么方法可以缩短这个吗?

aka*_*ppa 84

使用sprintf.

int sprintf ( char * str, const char * format, ... );
Run Code Online (Sandbox Code Playgroud)

将格式化数据写入字符串如果在printf上使用了格式,则使用与打印时相同的文本组成一个字符串,但不是打印,而是将内容作为C字符串存储在str指向的缓冲区中.

缓冲区的大小应足够大,以包含整个结果字符串(有关更安全的版本,请参阅snprintf).

在内容之后自动附加终止空字符.

在format参数之后,该函数至少需要格式化所需的其他参数.

参数:

str
Run Code Online (Sandbox Code Playgroud)

指向存储结果C字符串的缓冲区的指针.缓冲区应足够大以包含结果字符串.

format
Run Code Online (Sandbox Code Playgroud)

包含格式字符串的C字符串,其格式与printf中的格式相同(有关详细信息,请参阅printf).

... (additional arguments)
Run Code Online (Sandbox Code Playgroud)

根据格式字符串,该函数可能需要一系列附加参数,每个参数包含一个值,用于替换格式字符串中的格式说明符(或指向存储位置的指针,用于n).这些参数应至少与格式说明符中指定的值的数量一样多.函数忽略其他参数.

例:

// Allocates storage
char *hello_world = (char*)malloc(13 * sizeof(char));
// Prints "Hello world!" on hello_world
sprintf(hello_world, "%s %s!", "Hello" "world");
Run Code Online (Sandbox Code Playgroud)

  • 哎呀!如果可能的话,使用'n'变异函数.即snprintf.它们将使您计算缓冲区大小,从而防止超支. (31认同)
  • 是的,但他要求ANSI C功能,我不太确定snprintf是ansi还是posix. (7认同)
  • 啊.我错过了.但我被新标准所拯救:'n'变种在C99中是正式的.FWIW,YMMV等 (7认同)
  • @Joce - C99 snprintf()系列函数非常安全; 但是snprintf_s()系列确实有不同的行为(特别是关于如何处理trunction).但是 - 微软的_snprintf()函数并不安全 - 因为它可能会使得到的缓冲区无法终止(C99 snprintf()总是终止). (2认同)

cma*_*ter 13

如果您有一个POSIX-2008兼容系统(任何现代Linux),您可以使用安全便捷的asprintf()功能:它将malloc()为您提供足够的内存,您无需担心最大字符串大小.像这样使用它:

char* string;
if(0 > asprintf(&string, "Formatting a number: %d\n", 42)) return error;
log_out(string);
free(string);
Run Code Online (Sandbox Code Playgroud)

这是您以安全的方式构建字符串的最小努力.sprintf()您在问题中提供的代码存在严重缺陷:

  • 指针后面没有分配的内存.您正在将字符串写入内存中的随机位置!

  • 即使你写过

    char s[42];
    
    Run Code Online (Sandbox Code Playgroud)

    你会陷入深深的麻烦,因为你无法知道括号中的数字.

  • 即使您使用了"安全"变体snprintf(),您仍然会遇到字符串被截断的危险.写入日志文件时,这是一个相对较小的问题,但它有可能准确地切断本来有用的信息.此外,它将切断尾随的结束字符,将下一个日志行粘贴到未成功写入的行的末尾.

  • 如果你尝试在所有情况下使用组合malloc()snprintf()产生正确的行为,你最终得到的代码大约是我给出的两倍asprintf(),并且基本上重新编程了它的功能asprintf().


如果你正在寻找提供的包装log_out(),可以采取一个printf()风格的参数列表本身,你可以使用变种vasprintf(),这需要va_list作为参数.这是一个非常安全的包装器实现:

//Tell gcc that we are defining a printf-style function so that it can do type checking.
//Obviously, this should go into a header.
void log_out_wrapper(const char *format, ...) __attribute__ ((format (printf, 1, 2)));

void log_out_wrapper(const char *format, ...) {
    char* string;
    va_list args;

    va_start(args, format);
    if(0 > vasprintf(&string, format, args)) string = NULL;    //this is for logging, so failed allocation is not fatal
    va_end(args);

    if(string) {
        log_out(string);
        free(string);
    } else {
        log_out("Error while logging a message: Memory allocation failed.\n");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,`asprintf()` 既不是标准 C 2011 的一部分,也不是 POSIX 的一部分,甚至不是 POSIX 2008 或 2013。它是 TR 27431-2 的一部分:请参阅 [您使用 TR 24731 '安全' 函数吗?]( http://stackoverflow.com/questions/372980/do-you-use-the-tr-24731-safe-functions) (2认同)

Mic*_*urr 11

听起来我希望能够轻松地将使用printf样式格式创建的字符串传递给您已经拥有的带有简单字符串的函数.您可以使用stdarg.h工具创建包装函数vsnprintf()(根据您的编译器/平台,可能不是很容易获得):

#include <stdarg.h>
#include <stdio.h>

// a function that accepts a string:

void foo( char* s);

// You'd like to call a function that takes a format string 
//  and then calls foo():

void foofmt( char* fmt, ...)
{
    char buf[100];     // this should really be sized appropriately
                       // possibly in response to a call to vsnprintf()
    va_list vl;
    va_start(vl, fmt);

    vsnprintf( buf, sizeof( buf), fmt, vl);

    va_end( vl);

    foo( buf);
}



int main()
{
    int val = 42;

    foofmt( "Some value: %d\n", val);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

对于那些没有提供一系列snprintf()例程的良好实现(或任何实现)的平台,我成功地使用snprintf()Holger Weiss 的近公共域.


Tom*_*Tom 6

不要使用 sprintf。
它会溢出你的字符串缓冲区并使你的程序崩溃。
始终使用snprintf