2009-11-17 109 views
14

我想用类似于C如何将printf存储到变量中?

确实
char *tmp = (char *)sqlite3_column_text(selectstmt, 2); 
const char *sqlAnswers = printf("select key from answer WHERE key = %s LIMIT 5;", tmp); 

什么的printf后者是一个错误显然某物存储的格式化字符串。

+0

绑定参数更好的原因之一 – eckes 2017-12-08 17:33:49

回答

32

您可以使用sprintf,但不是单独(安全地)。在一个健全的系统上,使用两次snprintf,一次找出要使用的大小,第二次真正做到这一点。这取决于snprintf返回空间不足时所需的字符数。 Linux,BSD和C99兼容系统可以实现这一点; Windows通常不会。在后一种情况下,如果snprintf失败(在循环中直到snprintf成功),您需要分配一个初始缓冲区并分配一个较大的缓冲区。但在C99,下面的工作:

char *buf; 
size_t sz; 
sz = snprintf(NULL, 0, "select key from answer WHERE key = %s LIMIT 5;", tmp); 
buf = (char *)malloc(sz + 1); /* make sure you check for != NULL in real code */ 
snprintf(buf, sz+1, "select key from answer WHERE key = %s LIMIT 5;", tmp); 

然而,为构建SQL,它远远更好地使用prepared statements。他们避免了SQL注入漏洞(并且经常需要sprintf)。有了它们,您就可以准备好“回答where key =?limit 5;”中的选择键,然后使用参数tmp执行它。 SQL引擎放入字符串,并删除确保首先正确转义的需求。

+2

用于准备报表。 – Noldorin 2009-11-17 00:16:05

+0

@Noldorin,几乎没有准备好的陈述,你仍然可以给temp分配'3; drop table answer'。 – 2009-11-17 00:18:02

+1

Sane是符合C99标准的系统!一些C89实现提供了自己的'snprintf',它的行为不像C99描述的那样(返回值不一定是所需的长度)。 – pmg 2009-11-17 00:18:08

8

你想要sprintf()

char *sqlAnswers = malloc(SIZE_TO_HOLD_FINAL_STRING); 
sprintf(sqlAnswers, "select key from answer WHERE key = %s LIMIT 5;", tmp); 
+13

为安全起见,请始终使用snprintf()。 – 2009-11-17 00:10:45

+1

我可以同意这一点; +1。 – 2009-11-17 01:36:02

6

如果您使用的是GNU或BSD libc,您可能可以使用asprintf,它会自动分配正确大小的缓冲区。

#define _GNU_SOURCE 
#include <stdio.h> 
// ... 
char *sqlAnswers = NULL; 
int length = asprintf(&sqlAnswers,"select key from answer WHERE key = %s LIMIT 5;", tmp); 
free(sqlAnswers); 
+4

'asprintf'对于'sprintf的方便快捷(的malloc(的snprintf(...)))'绝招 - 我投用它提供了一个备用'asprintf'定义,如果你需要处理一个缺乏它的可悲的,过时的平台。 – ephemient 2009-11-17 15:52:27

0

在windows上,你可以使用sprintf_s,它增加了像Michael E所说的缓冲区溢出保护。

http://msdn.microsoft.com/en-us/library/ce3zzk1k(VS.80).aspx

+1

看来如果缓冲区太小,'sprintf_s'不会返回所需的字节数; GNU和BSD'snprintf'都可以。这是我所依赖的关键行为。 – 2009-11-17 04:20:04

1

我实际使用sqlite3_bind_text输入我的通配符,而不是通过sprintf的产生:

const char *sql1 = "select id, repA, key from iphone_reponse WHERE question_id = ?;"; 
sqlite3_stmt *selectstmt1; 
if(sqlite3_prepare_v2(database, sql1, -1, &selectstmt1, NULL) == SQLITE_OK) { 
    sqlite3_bind_text(selectstmt1, 1, [questionObj.key UTF8String], -1, SQLITE_TRANSIENT); 
-1

迈克尔·埃克斯特兰德代码是好的,但你将需要复制并粘贴各种倍。我用这个代码在一个函数

char *storePrintf (const char *fmt, ...) 
{ 
    va_list arg; 
    va_start(arg, fmt); 
    size_t sz = snprintf(NULL, 0, fmt, arg); 
    char *buf = (char *)malloc(sz + 1); 
    vsprintf(buf, fmt, arg); 
    va_end (arg); 
    return buf; 
} 

是否有问题,缓冲区溢出?到现在为止我没有问题。

编辑。

好吧,我有一个问题,因为我正在与Arduino合作。它使用内存,不要删除它,所以你需要在使用后删除它。