2009-11-17 50 views
14

Tôi muốn để lưu trữ một chuỗi định dạng sử dụng một cái gì đó tương tự như những gì printf làm trong C.làm thế nào để lưu trữ printf vào một biến?

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

Sau đó là một lỗi rõ ràng.

+0

Một trong những lý do tại sao tốt hơn là ràng buộc tham số – eckes

Trả lời

32

Bạn có thể làm điều đó với sprintf, nhưng không phải một mình (an toàn). Trên một hệ thống lành mạnh, sử dụng snprintf hai lần, một lần để tìm ra kích thước để sử dụng và lần thứ hai để thực sự làm điều đó. Điều này tùy thuộc vào số snprintf trả về số lượng ký tự cần thiết khi hết phòng. Các hệ thống tương thích với Linux, BSD và C99 thực hiện điều này; Windows thường không. Trong trường hợp thứ hai, bạn sẽ cần phải cấp phát một bộ đệm ban đầu và cấp phát một bộ đệm lớn hơn nếu snprintf không thành công (trong vòng lặp cho đến khi snprintf thành công). Nhưng trên C99, sau đây sẽ làm việc:

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); 

Tuy nhiên, để xây dựng SQL, nó là tốt hơn để sử dụng prepared statements. Chúng tránh các lỗ hổng SQL injection (và thường xuyên cần đến sprintf). Với chúng, bạn sẽ chuẩn bị câu lệnh "select key from answer where key =? Limit 5;", và sau đó thực thi nó với tham số tmp. Các công cụ SQL đặt trong chuỗi và loại bỏ sự cần thiết để đảm bảo nó được thoát đúng cách đầu tiên.

+2

+1 cho báo cáo đã chuẩn bị. – Noldorin

+0

@Noldorin, hầu như không có một tuyên bố chuẩn bị, bạn vẫn có thể gán '3; - trả lời bảng thả' thành tạm thời. –

+1

Sane là hệ thống tuân thủ tiêu chuẩn C99! Một số triển khai C89 cung cấp 'snprintf' của riêng chúng mà không hoạt động như mô tả C99 (giá trị trả về không nhất thiết phải là độ dài yêu cầu). – pmg

8

Bạn muốn sprintf().

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

Luôn sử dụng 'snprintf()' để đảm bảo an toàn. –

+1

Tôi có thể đồng ý với điều đó; +1. –

6

Nếu bạn đang sử dụng gnu hoặc BSD libc, bạn có thể sử dụng asprintf, giúp phân bổ bộ đệm có kích thước chính xác một cách tự động.

#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' là một phím tắt thuận tiện cho' sprintf (malloc (snprintf (...))) 'trick - Tôi bỏ phiếu cho việc sử dụng nó và cung cấp một định nghĩa dự phòng 'asprintf' nếu bạn phải đối phó với một nền tảng buồn, lỗi thời, thiếu nó. – ephemient

0

Trên cửa sổ, bạn có thể sử dụng sprintf_s để thêm tính năng bảo vệ tràn bộ đệm như Michael E đang nói.

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

+1

Có vẻ như 'sprintf_s' không trả lại số byte cần thiết nếu bộ đệm quá nhỏ; GNU và BSD 'snprintf' đều làm. Đó là hành vi chính mà tôi phụ thuộc vào. –

1

Tôi thực sự sử dụng sqlite3_bind_text để nhập ký tự đại diện của tôi thay vì tạo ra thông qua 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

Các Michael Ekstrand code đang tốt, nhưng bạn sẽ cần phải sao chép và dán nó khác nhau lần. Tôi sử dụng mã này trong một hàm

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; 
} 

Có vấn đề với tràn bộ đệm không? Cho đến bây giờ tôi không có vấn đề với nó.

Chỉnh sửa.

Ok, tôi gặp vấn đề vì tôi đang làm việc với Arduino. Nó sử dụng bộ nhớ và không thả nó, vì vậy bạn cần phải xóa nó sau khi sử dụng.

Các vấn đề liên quan