2009-11-17 10 views
14

Voglio memorizzare una stringa formattata utilizzando qualcosa di simile a quello che fa printf in C.come conservare printf in una variabile?

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

Quest'ultimo è un errore evidente.

+0

Una delle ragioni per cui è meglio per legare i parametri – eckes

risposta

32

È possibile farlo con sprintf, ma non da solo (in sicurezza). Su un sistema corretto, utilizzare snprintf due volte, una volta per scoprire la dimensione da utilizzare e la seconda volta per farlo effettivamente. Questo dipende da snprintf che restituisce il numero di caratteri necessari quando si esaurisce la stanza. I sistemi compatibili con Linux, BSD e C99 fanno questo; Windows in genere non lo fa. In quest'ultimo caso, sarà necessario allocare un buffer iniziale e allocarne uno più grande se snprintf ha esito negativo (in un ciclo fino al snprintf succede). Ma sul C99, il seguente funziona:

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

Tuttavia, per la costruzione di SQL, è molto meglio usare prepared statements. Evitano le vulnerabilità di SQL injection (e frequentemente la necessità di sprintf). Con loro, prepareresti la frase "seleziona chiave dalla risposta dove chiave =? Limite 5;", quindi eseguila con il parametro tmp. Il motore SQL inserisce la stringa e rimuove la necessità di assicurarsi che sia preceduto da un escape corretto.

+2

+1 per le dichiarazioni preparate. – Noldorin

+0

@Noldorin, difficilmente una dichiarazione preparata, è ancora possibile assegnare '3; - drop table answer' a temp. –

+1

Sane è un sistema conforme allo standard C99! Alcune implementazioni C89 offrono il proprio 'snprintf' che non si comporta come descritto in C99 (il valore restituito non è necessariamente la lunghezza richiesta). – pmg

8

Volete sprintf().

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

Usare sempre 'snprintf()' per la sicurezza. –

+1

Sono d'accordo con questo; +1. –

6

Se si utilizza gnu o BSD libc, è possibile utilizzare asprintf, che assegna automaticamente un buffer delle dimensioni corrette.

#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' è una comoda scorciatoia per il trucco' sprintf (malloc (snprintf (...))) - Io voto per usare esso e fornisce una definizione di 'asprintf' di riserva se si ha a che fare con una piattaforma triste e obsoleta che non ce l'ha. – ephemient

0

In Windows è possibile utilizzare sprintf_s che aggiunge protezione buffer overflow come Michael E stava dicendo.

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

+1

Sembra che 'sprintf_s' non restituisca il numero di byte richiesti se il buffer è troppo piccolo; GNU e BSD 'entrambi fanno snprintf'. Questo era il comportamento chiave su cui stavo dipendendo. –

1

Sono in realtà usando sqlite3_bind_text di inserire il mio jolly invece di generare attraverso 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

Il codice Michael Ekstrand è buona, ma sarà necessario copiare e incollare vari volte. Io uso questo codice in una funzione

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

Ha un problema con l'overflow del buffer? Fino ad ora non ho problemi con esso.

Modifica.

Ok, ho un problema, perché sto lavorando con Arduino. Usa la memoria e non lasciarla cadere, quindi è necessario eliminarla dopo l'uso.