PDA

View Full Version : [C++] SQLite - inserire/estrarre jpg


Emaborsa
17-04-2011, 16:36
Sono 3 giorni che studio come fare in C++, ho trovato MOLTI articoli ma non ne vengo a capo. Ho trovato esempi come QUESTO (http://www.dreamincode.net/forums/topic/122300-sqlite-in-c/), QUESTO (http://www.yolinux.com/TUTORIALS/SQLite.html#BLOB) e QUESTO (http://www.sqlite.org/cvstrac/wiki?p=BlobExample), mi sono letto molte cose su http://www.sqlite.org e mille commenti di altre persone.
L'inserimento sono riuscito, ma non riesco a fare ad estrarre il jpg e poi poterlo usare come variabile in una classe C++. Provengo da Java, e il C/C++ mi da ancora molte grane.

tomminno
17-04-2011, 18:19
Dovresti postare un pò di codice, nei link che hai indicato c'è la soluzione al tuo problema.

Emaborsa
17-04-2011, 18:47
Ho notato che l'insertion da me scritto non funzionava.
Si lo so che negli esempi c'è la soluzione, ma io dovrei modificarli in base alle mie classi e non ci riesco. In pratica nel DB ho un TABLE con 4 colonne. Ogni colonna è un attributo della mia classe Item:
#include "Item.h"

using namespace Tamagotchi;
using namespace std;

Item::Item(string aName, int aPrice, int anHappiness, FILE aPicture){
name=aName;
price=aPrice;
happiness=anHappiness;
picture = aPicture;
}

int Item::getPrice(){
return price;
}

int Item::getHappiness(){
return happiness;
}

string Item::getName(){
return name;
}

FILE Item:: getPicture(){
return picture;
}



In pratica io dovrei andare a interrogare il DB in modo che mi ritorni un Oggetto di tipo Item. Il mio problema è il "ricreare" il file, con il resto non avrei problemi.

tomminno
18-04-2011, 09:17
Si ma serve il codice che hai scritto per interrogare sqlite :)

Emaborsa
18-04-2011, 12:30
Si ma serve il codice che hai scritto per interrogare sqlite :)

...è proprio quello che mi manca e serve.

tomminno
18-04-2011, 14:16
...è proprio quello che mi manca e serve.

Potresti cominciare copiando quello che hai trovato no?
Ad esempio questo è un esempio funzionanate: http://www.yolinux.com/TUTORIALS/SQLite.html#BLOB

Emaborsa
18-04-2011, 14:24
Potresti cominciare copiando quello che hai trovato no?
Ad esempio questo è un esempio funzionanate: http://www.yolinux.com/TUTORIALS/SQLite.html#BLOB

Più tardi ci proverò di nuovo....ti faccio sapere.

Emaborsa
18-04-2011, 15:50
Allora, l'insertion che avevo fatto (ora cancellato perchè non mi serve più) era sbagliato perchè, se non erro, il quarto parametro di sqlite3_bind_blob() deve essere la dimensione del file in bytes. Forse se avessi cambiato quello, punzionava.
Comunque dato che l'insertion non serviva per la funzionalità del mio programma, ho optato per creare il DB a mano usando sqliteman di Linux. Ora però mi serve l'algoritmo di interrogazione. Partiamo dall'inizio. Se i valori restituiti fossero semplici valori (int o string) potrei usare semplicemente sqlite3_exec(). Dato che il dato estratto è un blob, non so come muovermi. Mi stavo orientando sul "readBlob()" dell'altro ESEMPIO (http://www.sqlite.org/cvstrac/wiki?p=BlobExample).

Emaborsa
18-04-2011, 19:27
Bene, me lo sono studiato e ho scritto del codice:

ostringstream oss;
int aNumber = getItemRandomNumber();
oss << aNumber;
string number = oss.str();
string aQuery = "SELECT image FROM items WHERE number = " + number + ";";
unsigned char** ptrPicture;
int* ptrPictureSize;
const char* bQuery = &aQuery[0];
sqlite3_stmt* pStmt;
int rc;
const char* ptrValue = &oss.str()[0];



do {
rc = sqlite3_prepare(databaseConnection, bQuery, -1, &pStmt, 0);
if (rc != SQLITE_OK) {
cerr << "error by sqlite3_prepare";
}

sqlite3_bind_text(pStmt, 1, ptrValue, -1, SQLITE_STATIC);

rc = sqlite3_step(pStmt);
if (rc == SQLITE_ROW) {

*ptrPictureSize = sqlite3_column_bytes(pStmt, 0);
*ptrPicture = (unsigned char *) malloc(*ptrPictureSize);
memcpy(*ptrPicture, sqlite3_column_blob(pStmt, 0), *ptrPictureSize);
}


rc = sqlite3_finalize(pStmt);


} while (rc == SQLITE_SCHEMA);

Però se avvio, non succede nulla, come se andasse in crash. Per fare dei test ho messo dei "cout <<" sparsi per il codice e sono arrivato alla conclusione ch eil problema sta nelle righe segnalate in rosso. In effetti se le "commento", il codice viene eseguito tutto, ovviamnete senza l'assegnazione al puntatore.
Idee?

Emaborsa
18-04-2011, 22:43
Dopo altre 3 ore sono arrivato a fare qualche modifica:

ostringstream oss;
int aNumber = getItemRandomNumber();
oss << aNumber;
string number = oss.str();
string aQuery = "SELECT image FROM items WHERE number = " + number + ";";
FILE* ptrPicture;
int* ptrPictureSize;
const char* bQuery = &aQuery[0];
sqlite3_stmt* pStmt;
int rc;
const char* ptrValue = "newfile.jpg";


do {
rc = sqlite3_prepare(databaseConnection, bQuery, -1, &pStmt, 0);
if (rc != SQLITE_OK) {
cerr << "error by sqlite3_prepare in getRandomItem";
}

sqlite3_bind_blob(pStmt, 1, ptrValue, -1, SQLITE_STATIC);

rc = sqlite3_step(pStmt);
if (rc == SQLITE_ROW) {

*ptrPictureSize = sqlite3_column_bytes(pStmt, 0);
ptrPicture = (FILE*) malloc(*ptrPictureSize);
memcpy(ptrPicture, sqlite3_column_blob(pStmt, 0), *ptrPictureSize);

}


rc = sqlite3_finalize(pStmt);


} while (rc == SQLITE_SCHEMA);


Ora se lancio, non da errori e non va in crash. La mia domanda... Come faccio a vedere se veramente mi prende l'immagine dal DB e mi crea l'oggetto di tipo FILE?

Emaborsa
20-04-2011, 21:56
FINALMENTE penso di aver capito. Ho scritto il seguente metodo:

Item* Database::getRandomItem() { //TODO

//getting a random number from the table items
int aNumber = getRandomNumber("items");
string number;

//converting the random number to a string
{
ostringstream oss;
oss << aNumber;
number = oss.str();
}

//getting the name price and happiness value from
//the table itemns, accordingly to the random number
//and converting the price and hapiness to int
string aQuery =
"SELECT name, price, happiness, image, filename FROM items WHERE number = "
+ number + ";";
sqlite3_stmt *pStmt;

char* ptAQuery = &aQuery[0];
if (sqlite3_prepare_v2(databaseConnection, ptAQuery, -1, &pStmt, 0)
!= SQLITE_OK) {
cerr << "db error (preparev2 in getRandomItem): " << sqlite3_errmsg(
databaseConnection) << endl;
}
if (sqlite3_step(pStmt) != SQLITE_ROW) {
cerr << "db error (step in getRandomItem): " << sqlite3_errmsg(
databaseConnection) << endl;
}

const unsigned char* ptName = sqlite3_column_text(pStmt, 0);
char* ptNameChar = (char*) ptName;
string nameString = ptNameChar;

int price = sqlite3_column_int(pStmt, 1);

int happiness = sqlite3_column_int(pStmt, 2);

int imageSize = sqlite3_column_bytes(pStmt, 3);

FILE* ptImage = (FILE*) malloc(imageSize);
memcpy(ptImage, sqlite3_column_blob(pStmt, 3), imageSize);

const unsigned char* ptFilename = sqlite3_column_text(pStmt, 4);
char* ptFilenameChar = (char*) ptFilename;
string filenameString = ptFilenameChar;

sqlite3_finalize(pStmt);

return new Item(filenameString, price, happiness, ptImage);

}


Ora ho due dubbi:
1. dato che ptImage è un puntatore, una volta che uso sqlite3_finalize(pStmt); il puntatore perde il valore, o grazie a malloc e memcpy resta?
2. come faccio a testare se fuzniona?
Chiedo "gentilmente" due risposte :D

tomminno
20-04-2011, 23:25
Ora ho due dubbi:
1. dato che ptImage è un puntatore, una volta che uso sqlite3_finalize(pStmt); il puntatore perde il valore, o grazie a malloc e memcpy resta?
2. come faccio a testare se fuzniona?
Chiedo "gentilmente" due risposte :D

Risposte rapide:
1) Non funzionerà certamente perchè stai cercando di allocare tramite malloc un FILE, operazione che è destinata miseramente a fallire, dovresti invece allocare un buffer.
2)Un debugger?

Una osservazione: quando copi il risultato di sqlite3_column_text in una stringa non hai bisogno di effettuare tutti quei passaggi:

string nameString((const char*)sqlite3_column_text(pStmt, 0));

Infine hai eseguito una malloc senza la relativa free... :)

Emaborsa
21-04-2011, 07:40
che potevo scrivere direttamente string non lo sapevo, pensavo di dover fare le conversioni. Grazie per l'osservazione.
Che dovevo "liberare" dopo malloc lo sapevo, ma me ne sono scordato. Per il fatto del buffer...non ho idea di come fare. Hai voglia si spiegarmi?

tomminno
21-04-2011, 10:03
che potevo scrivere direttamente string non lo sapevo, pensavo di dover fare le conversioni. Grazie per l'osservazione.
Che dovevo "liberare" dopo malloc lo sapevo, ma me ne sono scordato. Per il fatto del buffer...non ho idea di come fare. Hai voglia si spiegarmi?

Io preferisco usare vector così mi evito di dover gestire la memoria a mano :D

vector<unsinged char>ptImage(imageSize);
memcpy(&ptImage[0], sqlite3_column_blob(pStmt, 3), imageSize);

Emaborsa
21-04-2011, 10:35
Io preferisco usare vector così mi evito di dover gestire la memoria a mano :D

...non ho capito.
Non manca qualcosa? vector<unsinged char>ptImage(imageSize);?

Emaborsa
21-04-2011, 12:49
1) Non funzionerà certamente perchè stai cercando di allocare tramite malloc un FILE, operazione che è destinata miseramente a fallire, dovresti invece allocare un buffer.

Mi spieghi anche questa? Se leggo QUI (http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/) tice che posso fare il cast "to the desired type".

tomminno
21-04-2011, 20:30
...non ho capito.
Non manca qualcosa? vector<unsinged char>ptImage(imageSize);?

Cosa dovrebbe mancare?
Forse il namespace std se non hai dichiarato lo using prima e ovviamente l'include dell'header vector:

#include <vector>
...
std::vector<unsinged char>ptImage(imageSize);

tomminno
21-04-2011, 20:33
Mi spieghi anche questa? Se leggo QUI (http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/) tice che posso fare il cast "to the desired type".

Si però FILE è un "oggetto" non un array.
Contiene informazioni riguardanti uno stream di dati e deve essere istanziato ed elaborato da funzioni presenti in cstdio.h.
Non hai modo programmaticamente di istanziare correttamente tutte le strutture dati interne all' "oggetto" FILE, nè tanto meno può farlo malloc, che semplicemente riserva un'area di memoria.

Emaborsa
21-04-2011, 20:48
Cosa dovrebbe mancare?
Forse il namespace std se non hai dichiarato lo using prima e ovviamente l'include dell'header vector:

#include <vector>
...
std::vector<unsinged char>ptImage(imageSize);


...boh, non la capisco. Saranno le mie scarse conoscenze di C++.


Si però FILE è un "oggetto" non un array.
Contiene informazioni riguardanti uno stream di dati e deve essere istanziato ed elaborato da funzioni presenti in cstdio.h.
Non hai modo programmaticamente di istanziare correttamente tutte le strutture dati interne all' "oggetto" FILE, nè tanto meno può farlo malloc, che semplicemente riserva un'area di memoria.

ok, capito....allora come faccio? ok posso fare il cast in un char*, ma poi con quello cosa ci faccio? Al momento ho questo codice:

char* ptImage = (char*) malloc(imageSize);

memcpy(ptImage, sqlite3_column_blob(pStmt, 3), imageSize);

const unsigned char* ptFilename = sqlite3_column_text(pStmt, 4);
const char* ptFilenameChar = (const char*) ptFilename;
ofstream outfile (ptFilenameChar,ofstream::binary);
outfile.write (ptImage,imageSize);

Questo l'ho fatto per vedere se il codice è OK, e in effetti funziona, dato che mi crea il jpg su disco fisso. Io però ho bisogno dell'oggetto FILE*. C'è un modo per farlo o devo scrivere sul disco (come attualmente fa) e poi aprirlo con fopen?

tomminno
21-04-2011, 22:02
...boh, non la capisco. Saranno le mie scarse conoscenze di C++.


Semplicemente quel codice costruisce un oggetto vettore di unsigned char (è uguale anche usando char) ampio imageSize.
Il vantaggio di vector è che è compatibile con gli array del C.


ok, capito....allora come faccio? ok posso fare il cast in un char*, ma poi con quello cosa ci faccio? Al momento ho questo codice:

char* ptImage = (char*) malloc(imageSize);

memcpy(ptImage, sqlite3_column_blob(pStmt, 3), imageSize);

const unsigned char* ptFilename = sqlite3_column_text(pStmt, 4);
const char* ptFilenameChar = (const char*) ptFilename;
ofstream outfile (ptFilenameChar,ofstream::binary);
outfile.write (ptImage,imageSize);

Questo l'ho fatto per vedere se il codice è OK, e in effetti funziona, dato che mi crea il jpg su disco fisso. Io però ho bisogno dell'oggetto FILE*. C'è un modo per farlo o devo scrivere sul disco (come attualmente fa) e poi aprirlo con fopen?

Perchè vuoi proprio farti del male utilizzando FILE*? :)
In tal caso devi usare le funzioni di io su file del C al posto di ofstream tipo:

FILE * fp = fopen(ptFilenameChar, "wb");
fwrite (ptImage, 1 , imageSize , fp );
...
fclose(fp);

Emaborsa
21-04-2011, 22:18
Perchè vuoi proprio farti del male utilizzando FILE*? :)
In tal caso devi usare le funzioni di io su file del C al posto di ofstream tipo:

FILE * fp = fopen(ptFilenameChar, "wb");
fwrite (ptImage, 1 , imageSize , fp );
...
fclose(fp);


...non è che VOGLIO, mi sembrava la cosa più semplice. Dal database mi torna un char* OK fino a qui ci siamo, ma poi se volessi inserirla in GUI, dimmi come sarebbe più semplice; non saprei come fare (comuqnue questo è OT) :/

tomminno
22-04-2011, 15:23
...non è che VOGLIO, mi sembrava la cosa più semplice.


Hai già usato la libreria standard del c++ per scrivere su file, non mi pare che ti serva andare ad usare anche FILE.


Dal database mi torna un char* OK fino a qui ci siamo, ma poi se volessi inserirla in GUI, dimmi come sarebbe più semplice; non saprei come fare (comuqnue questo è OT) :/

Secondo me con Qt.
Tra l'altro integrano direttamente Sqlite pertanto non avresti nemmeno bisogno di utilizzare le api dirette.

Emaborsa
22-04-2011, 19:06
...mi hanno consigliato di tenerlo, invece che in FILE, in bytearray.
Tuo parere?

Emaborsa
08-05-2011, 15:05
Ragazzi sono ancora alle prese con sti blob ricavati da sqlite db. Come detto sopra, riesco ad estrarli, ma non riesco ancora a creare un array o qualcos'altro di simile per poi mostrarli a livello GUI.
Chi ha voglia (e conoscenze) per aiutarmi?
in java sarei capace, creerei un ByteArray e da quello creerei un ImageIcon per poi mostrarlo in un frame. A me serve in C++, per l'esattezza QT. QT ha anche la class QByteArray, ma mi manca il passaggio da DB a questa QByteArray.

tomminno
09-05-2011, 08:00
Ragazzi sono ancora alle prese con sti blob ricavati da sqlite db. Come detto sopra, riesco ad estrarli, ma non riesco ancora a creare un array o qualcos'altro di simile per poi mostrarli a livello GUI.
Chi ha voglia (e conoscenze) per aiutarmi?
in java sarei capace, creerei un ByteArray e da quello creerei un ImageIcon per poi mostrarlo in un frame. A me serve in C++, per l'esattezza QT. QT ha anche la class QByteArray, ma mi manca il passaggio da DB a questa QByteArray.

Con QT, non hai bisogno di passare da QByteArray, una volta che hai l'array dei dati estratti da Sqlite (il ptImage del codice precedente), passi questo array al costruttore di QImage. L'inconveniente è che in questo modo devi conoscere il formato e la dimensione dell'immagine.
Dato che ti sei salvato l'immagine in un file, conviene usare il costruttore di QImage che legge da file.