感受一下C++操作SQLite有多麻煩

感受一下C++操作SQLite有多麻煩

學(xué)程序設(shè)計的估計都要學(xué) C/C++砾莱。當(dāng)年學(xué)的時候無非是覺得指針用起來有點暈,用的時候要考慮清楚腊瑟,要十分小心,并沒有覺得有多難魔策,也沒覺得C/C++用起來會很麻煩河胎。

在后來的應(yīng)用的場景中游岳,慢慢遠離了C++其徙,很快上手了C#。直到最近唾那,為了追求效率,想拿C++試一把期犬。結(jié)果是效率提升到?jīng)]看出太多避诽,其麻煩程度讓我徹底放棄了。

就不拿python這種極端的做對比了鲤妥,用C#做對比足夠了。以查詢返回多條記錄的操作為例棉安。

C#的方法

C#查詢SQLite并返回多條記錄的操作核心代碼一般是這樣的:

//第一,聲名數(shù)據(jù)庫連接字符串衷模,關(guān)鍵參數(shù)是數(shù)據(jù)庫文件的路徑和UTF8的編碼
SQLiteConnection conn = new SQLiteConnection("Data Source=database.db;Pooling=true;UTF8Encoding=True;Version=3");
//聲名一個數(shù)據(jù)讀取對象
SQLiteDataReader dr;
//聲名一個命令操作對象菇爪,關(guān)鍵參數(shù)是SQL語句字符串
SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM TABLE", conn);
//打開連接
conn.Open();
//獲取返回數(shù)據(jù)指針
dr = cmd.ExecuteReader();
//通過whi循環(huán)分條讀取記錄,針對不同類型調(diào)用不同的讀取方法熙揍。
if (dr.HasRows){
  while (dr.Read()){
    dr.GetString(0);
    dr.GetInt32(1);
    dr.GetDouble(2);
  }
}
//關(guān)閉連接
dr.Close();
conn.Close();  

是不是看上去C#的操作也不是特別簡單呢氏涩?但好在邏輯上非常清晰,每個方法的參數(shù)都實實在在意系,沒有什么其他感覺上是多余的東西饺汹。而且,如果對比了C++的兜辞,就會知道C#是多么的輕而易舉。

C++的方法

C++操作SQLite就復(fù)雜就不提他在前期配置和生成Linker的一堆麻煩了凶硅,只看代碼扫皱。

首先,數(shù)據(jù)庫連接要這樣打開:

sqlite3* db = NULL;//數(shù)據(jù)庫指針
sqlite3_stmt* stmt = NULL;//用于一些查詢的返回值
char *zErrMsg = 0;//保存返回的錯誤信息
sqlite3_open("dag.db", &db);//打開數(shù)據(jù)庫氢妈,把打開的指針傳遞給db

C++到是有很多方法來實現(xiàn)多條返回記錄的獲取扰才。

回調(diào)函數(shù)就是一種。很幸運蕾总,我是不知道啥叫回調(diào)函數(shù)的粥航,只知道可以向下面這樣用递雀。

//sqlite每查到一條記錄蚀浆,就調(diào)用一次這個回調(diào)函數(shù)。
int callback(void*para , int nCount, char** pValue, char** pName) {
     string s; 
      for(int i=0;i<nCount;i++){ 
          s+=pName[i]; 
          s+=":"; 
          s+=pValue[i]; 
          s+="\n";  
    } 
    cout<<s<<endl; 
    return 0; 
 }
// para 是你在 sqlite3_exec 里傳入的 void* 參數(shù), 通過 para 參數(shù)可以傳入一些特殊的指針(比如類指  針杨凑、結(jié)構(gòu)指針)摆昧,然后在這里面強制轉(zhuǎn)換成對應(yīng)的類型。
// nCount 是這一條記錄有多少個字段伺帘。
// char** pValue 是個關(guān)鍵值,查出來的數(shù)據(jù)都保存在這里伪嫁,它是個1維數(shù)組(不要以為是2維數(shù)組)偶垮,每一個元素都是一個 char* 值,是一個字段內(nèi)容晶伦。
// char** pName 跟 pValue 是對應(yīng)的啄枕,表示這個字段的字段名稱, 也是個1維數(shù)組频祝。

有了這個回調(diào)函數(shù)的聲名,就可以在主程序段用下面語句查詢了:

rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
//第 1 個參數(shù)是前面 open 函數(shù)得到的指針常空。 
//第 2 個參數(shù)是一條 sql 語句盖溺。 
//第 3 個參數(shù)上面聲名的回調(diào)函數(shù),當(dāng)這條語句執(zhí)行之后昆禽,sqlite3 會去調(diào)用。 
//第 4 個參數(shù)是你所提供的指針醉鳖,你可以傳遞任何一個指針參數(shù)到這里,這個參數(shù)最終會傳到回調(diào)函數(shù)里面壮韭,如果不需要傳遞指針給回調(diào)函數(shù)纹因,可以填 NULL。
//第 5 個參數(shù)是錯誤信息瞭恰。注意是指針的指針。sqlite3 里面有很多固定的錯誤信息是牢,執(zhí)行失敗時可以查閱這個指針陕截。
//這條sql語句是否正常執(zhí)行還可以通過函數(shù)的返回值判斷,并打印錯誤信息
if (rc != SQLITE_OK){
  fprintf(stderr, "SQL error: %s\n", zErrMsg);
  sqlite3_free(zErr.Msg);
}

好了社搅,是不是感覺和C#很不一樣乳规。

當(dāng)然,C++也有和C#相似一點的操作方法:

//先使用 sqlite3_prepare_v2 執(zhí)行 sql 語句
sqlite3_prepare_v2(db, sql, 512, &stmt, NULL);
//再使用 sqlite3_step 遍歷查詢返回的結(jié)果
//這里可以用循環(huán)進行遍歷
if (sqlite3_step(stmt) != SQLITE_ROW)
  cout << "qurey error" << endl;
//每次獲取的結(jié)果笙以,使用 sqlite3_column 系列函數(shù)獲取內(nèi)容,不同類型要使用不同的函數(shù)猖腕,第二個參數(shù)是查詢的字段索引
sqlite3_column_text(stmt, 0);
//這一系列函數(shù)類似于C#中的GetString, GetInt32恨闪,如下面這些:
double sqlite3_column_double(sqlite3_stmt*, int iCol); 
int sqlite3_column_int(sqlite3_stmt*, int iCol); 
//一共有十多個

好了,如果使用后面這種方法到也還算是不錯了老玛。

然而,如果你的數(shù)據(jù)庫里有中文麸粮。不好意思,很有可能會出現(xiàn)亂碼余素。因為C++是ASCII,數(shù)據(jù)庫一般是UTF-8威根。這個問題在C#中只是一個連接字符的參數(shù)設(shè)置视乐。本以為C++差不多的×裘溃可現(xiàn)實并不是這樣伸刃。C++要自行轉(zhuǎn)碼∨趼可令人不解的是,這個轉(zhuǎn)碼竟然要這么長的代碼挚币,唯一可以慶幸的是扣典,直接copy下面的函數(shù)就行了。

//UTF-8轉(zhuǎn)Unicode 
std::wstring Utf82Unicode(const std::string& utf8string){
    int widesize = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, NULL, 0);
    if (widesize == ERROR_NO_UNICODE_TRANSLATION){
        throw std::exception("Invalid UTF-8 sequence.");
    }
    if (widesize == 0){
        throw std::exception("Error in conversion.");
    }
    std::vector<wchar_t> resultstring(widesize);
    int convresult = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, &resultstring[0], widesize);
    if (convresult != widesize) {
        throw std::exception("La falla!");
    }
    return std::wstring(&resultstring[0]);
}
//unicode 轉(zhuǎn)為 ascii 
string WideByte2Acsi(wstring& wstrcode){
    int asciisize = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, NULL, 0, NULL, NULL);
    if (asciisize == ERROR_NO_UNICODE_TRANSLATION){
        throw std::exception("Invalid UTF-8 sequence.");
    }
    if (asciisize == 0){
        throw std::exception("Error in conversion.");
    }
    std::vector<char> resultstring(asciisize);
    int convresult = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, &resultstring[0], asciisize, NULL, NULL);
    if (convresult != asciisize){
        throw std::exception("La falla!");
    }
    return std::string(&resultstring[0]);
}
//utf-8 轉(zhuǎn) ascii 
string UTF_82ASCII(string& strUtf8Code){
    string strRet("");
    //先把 utf8 轉(zhuǎn)為 unicode 
    wstring wstr = Utf82Unicode(strUtf8Code);
    //最后把 unicode 轉(zhuǎn)為 ascii 
    strRet = WideByte2Acsi(wstr);
    return strRet;
}
//ascii 轉(zhuǎn) Unicode 
wstring Acsi2WideByte(string& strascii){
    int widesize = MultiByteToWideChar(CP_ACP, 0, (char*)strascii.c_str(), -1, NULL, 0);
    if (widesize == ERROR_NO_UNICODE_TRANSLATION){
        throw std::exception("Invalid UTF-8 sequence.");
    }
    if (widesize == 0){
        throw std::exception("Error in conversion.");
    }
    std::vector<wchar_t> resultstring(widesize);
    int convresult = MultiByteToWideChar(CP_ACP, 0, (char*)strascii.c_str(), -1, &resultstring[0], widesize);
    if (convresult != widesize){
        throw std::exception("La falla!");
    }
    return std::wstring(&resultstring[0]);
}
//Unicode 轉(zhuǎn) Utf8 
std::string Unicode2Utf8(const std::wstring& widestring){
    int utf8size = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, NULL, 0, NULL, NULL);
    if (utf8size == 0){
        throw std::exception("Error in conversion.");
    }
    std::vector<char> resultstring(utf8size);
    int convresult = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, &resultstring[0], utf8size, NULL, NULL);
    if (convresult != utf8size){
        throw std::exception("La falla!");
    }
    return std::string(&resultstring[0]);
}
//ascii 轉(zhuǎn) Utf8 
string ASCII2UTF8(string& strAsciiCode){
    string strRet("");
    //先把 ascii 轉(zhuǎn)為 unicode 
    wstring wstr = Acsi2WideByte(strAsciiCode);
    //最后把 unicode 轉(zhuǎn)為 utf8 
    strRet = Unicode2Utf8(wstr);
    return strRet;
}

驚不驚喜,意不意外湿硝。是的,你沒看錯,他就是這么長任连。不僅如此,還要告訴你的是ASCII2UTF8函數(shù)的參數(shù)類型是string&,而且返回值類型是string繁涂。而 sqlite3column_text 的返回值類型是unsigned char*二驰。所以,轉(zhuǎn)碼需要先把 unsigned char* 用轉(zhuǎn)為 string矿酵,而轉(zhuǎn) string 要先轉(zhuǎn)char*,即 string((char*)sqlite3_column_text(stmt, 0))全肮。如果是用 cout 輸出的話棘捣,還得使用string 的 c_str() 方法,把 string 再轉(zhuǎn)為char*乍恐。

好了,有了這些資料百匆,用 C++ 操作 SQLite 差不多是足夠了瞧毙。但我是不會再用了,留給專業(yè)人士吧宙彪。我還是繼續(xù)我的C#和Python。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悲没,一起剝皮案震驚了整個濱河市男图,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栈戳,老刑警劉巖难裆,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镊掖,死亡現(xiàn)場離奇詭異亩进,居然都是意外死亡缩歪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門主籍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骗污,“玉大人,你說我怎么就攤上這事需忿。” “怎么了涕烧?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵汗洒,是天一觀的道長。 經(jīng)常有香客問我溢谤,道長,這世上最難降的妖魔是什么阀参? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任瞻坝,我火速辦了婚禮,結(jié)果婚禮上衙荐,老公的妹妹穿的比我還像新娘。我一直安慰自己忧吟,他們只是感情好斩披,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布胸嘴。 她就那樣靜靜地躺著,像睡著了一般乡话。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绑青,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音坏挠,去河邊找鬼邪乍。 笑死,一個胖子當(dāng)著我的面吹牛庇楞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛋褥,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼睛驳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乏沸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤痘昌,失蹤者是張志新(化名)和其女友劉穎炬转,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扼劈,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡荐吵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年赊瞬,在試婚紗的時候發(fā)現(xiàn)自己被綠了贼涩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡谤绳,死狀恐怖袒哥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堡称,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布桐臊,位于F島的核電站啄寡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挺物。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一砚著、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稽穆,春花似錦、人聲如沸舌镶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘤载。三九已至,卻和暖如春墨技,著一層夾襖步出監(jiān)牢的瞬間惩阶,已是汗流浹背扣汪。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工崭别, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人紊遵。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓暗膜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親学搜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,125評論 29 470
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法坯台,內(nèi)部類的語法,繼承相關(guān)的語法蜒蕾,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,506評論 1 51
  • 08/14/2017 周一 丁酉年 閏六月二十三日】 √靜√智√勇√仁√強√禮 - 小結(jié)首启。 - 明日計劃撤摸。 - 小...
    媽媽熊閱讀 161評論 0 0
  • 最近幾天追電視劇《人民的名義》,總算把它看完了钥飞。 總得來說,最后的結(jié)局還是符合我們的期望的代承,比如正與邪的對抗,最后...
    阿木拉閱讀 506評論 0 2