2009-11-17 213 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合作。它使用內存,不要刪除它,所以你需要在使用後刪除它。