step-4:Unicode版サンプル
追加したsqlite_exec16
が正しく動作するか、「sample.cpp」をUTF-16に書き換えて試運転を行います。ソリューションにコンソール・アプリケーション・プロジェクト「sample16」を追加し、step-2と同様に依存関係とインクルードパスを設定します。
ソースコード「sampe16.cpp」を追加し、「sample.cpp」のナカミをコピーして次のように手を加えます。以下は呼び出す関数sqlite3_xxx
を適宜 sqlite3_xxx16
に置き換え、文字列リテラルの先頭にUnicodeであることを示すL
を付加したものです。
#include <sqlite3.h> #include <iostream> #include <locale> #include <cassert> /* * sqlite3_exec から呼ばれるコールバック * カラム名と値を出力する */ int callback(void* arg, int columns, wchar_t** value, wchar_t** name) { std::wostream* stream = static_cast<std::wostream*>(arg); for ( int i = 0; i < columns; ++i ) { *stream << L'[' << name[i] << L'=' << value[i] << L"] "; } *stream << std::endl; return 0; } int main() { std::locale loc("japanese"); std::wcout.imbue(loc); std::wcerr.imbue(loc); sqlite3* db; /* ** databaseをオープン。 ** ファイル名を ":memory:" とすれば、on-memory データベースとなる。 */ int result; result = sqlite3_open16(L":memory:", &db); assert(result==SQLITE_OK); /* ** TABLE を生成する。 ** sqlite_exec の結果が SQLITE_OK でないとき、 ** エラーメッセージが errmsg に返される。 ** このエラーメッセージは sqlite_free で解放すべし。 */ wchar_t* errmsg; if ( sqlite3_exec16(db, L"CREATE TABLE IF NOT EXISTS わんくま ( ID INTEGER PRIMARY KEY, 名前 TEXT, 地域 TEXT)", 0, 0, &errmsg) != SQLITE_OK ) { std::wcerr << errmsg << std::endl; sqlite3_free(errmsg); sqlite3_close(db); return 1; } /* ** レコードを追加する。 ** sqlite3_prepare によってSQL文をコンパイル。 ** SQL文中の ? の箇所に sqlite3_bind_xxxx で ** 値をバインド(差し込み)する。 */ sqlite3_stmt* statement; sqlite3_prepare16(db, L"INSERT INTO わんくま (名前, 地域) VALUES ( ?, ? )", -1, &statement, NULL); const wchar_t* data[] = { L"επιστημη", L"横浜", L"えムナウ", L"東京", L"中", L"大阪", L"じゃんぬ", L"名古屋", L"なおこ(・∀・)", L"東京", 0, 0 }; /* ** 値のバインドと実行。 ** sqlite3_reset のに続いて sqlite3_bind_xxx によってバインドする。 ** このとき最初の ? はインデクス 1 であることに留意すべし。 ** 引き続いて sqlite3_step によって実行される。 ** 一連の実行の後、sqlite3_finalize すること。 */ for ( const wchar_t** p = data; *p != 0;) { sqlite3_reset(statement); sqlite3_bind_text16(statement, 1, *p, -1, SQLITE_STATIC); ++p; sqlite3_bind_text16(statement, 2, *p, -1, SQLITE_STATIC); ++p; sqlite3_step(statement); } sqlite3_finalize(statement); /* ** QUERY を行う。 ** sqlite3_exec の結果がコールバック関数に引き渡される */ result = sqlite3_exec16(db, L"SELECT * FROM わんくま", callback, &std::wcout, &errmsg); assert( result == SQLITE_OK ); /* ** databaseをクローズする。 */ sqlite3_close(db); return 0; }
コンパイルして実行しましょう。正しい出力が得られたでしょうか。
まとめ
以上駆け足でSQLiteのビルドとUTF-16(Unicode)化パッチ(?)の手順について解説しました。後半のUTF-16化はあるタクラミの伏線となっています。
SQLiteはお手軽軽量なDBとしてさまざまなアプリケーションに使えるものと思います。このお手軽さを享受できるのがC/C++だけではもったいない。C++/CLIで薄いラッパーを被せれば、C#やVB.NETから使えるようにできるはずですが、そのお話はまたいずれ。