如何使用本机 sqlite 库而不是 QSql 从 sqlite 插入和检索 QString

Posted

技术标签:

【中文标题】如何使用本机 sqlite 库而不是 QSql 从 sqlite 插入和检索 QString【英文标题】:How to insert and retrieve QString from sqlite using native sqlite library instead of QSql 【发布时间】:2015-02-06 13:39:21 【问题描述】:

我在一个使用 Qt 的 C++ 项目中使用本机 sqlite 库(合并 1file),我围绕它创建了一个简单的包装器,但我在插入和选择长 UTF-8 文本时遇到了麻烦。

我用这段代码插入数据:

SqlResult *result = new SqlResult();
sqlite3_stmt *statement;
int current_parameter = 1;
this->LastStatement = sql;
int x = sqlite3_prepare_v2(this->db, sql.toUtf8().constData(), sql.length() + 1, &statement, NULL);
foreach (QString text, parameter)

    ////////////// here is problem ///////////////
    x = sqlite3_bind_text(statement, current_parameter++, text.toUtf8().constData(), -1, SQLITE_STATIC);
    if (!this->Evaluate(x))
    ...

如您所见,我正在使用 SQL 变量 ?,其文本绑定到使用函数 sqlite3_bind_text(statement, current_parameter++, text.toUtf8().constData(), -1, SQLITE_STATIC),该函数应获取 QString 文本的值并将其转换为 utf8 const char *。然而,我在数据库中获得的文本部分是垃圾(当我使用一些 sqlite 浏览器应用程序时,我可以看到一些奇怪的符号)。

这是我用来转换 const char * 的代码,我从 sqlite3_column_text 获得

static QString StringFromUnsignedChar( const unsigned char *str )

    std::string temp = std::string(reinterpret_cast<const char*>(str));
    return QString::fromUtf8(temp.c_str());

我得到的文本与我在 sqlite 管理器中看到的“垃圾”相同。所以我想问题出在插入过程中,并且选择可能有效。怎么了?如何正确使用 sqlite3_bind_text 函数和 QString

附:我宁愿不使用 Qt 自己的 sqlite 实现,主要是出于兼容性目的(在 linux 上我使用 Qt4,在 windows Qt5 上,我希望在任何地方都有相同的 sqlite 库以实现可移植性)

【问题讨论】:

会不会是double UTF-8 encoding的问题? 看来我已经找到了这个bug,问题是我需要使用SQL_TRANSIENT,以便sqlite创建一个const char *数组的副本,因为它是在堆栈上分配的,所以很快就会被删除随着函数调用结束。 sqlite 将删除的字符串存储到数据库中,这是大多数字符变成垃圾的原因。 太棒了。发布为答案。 【参考方案1】:

问题在于我自己的代码,用法正确但实现不正确。这一行:

sqlite3_bind_text(statement, current_parameter++, text.toUtf8().constData(), -1, SQLITE_STATIC);

需要

sqlite3_bind_text(statement, current_parameter++, text.toUtf8().constData(), -1, SQLITE_TRANSIENT);

如文档所述:

BLOB 和字符串绑定接口的第五个参数是 析构函数用于在 SQLite 处理后处理 BLOB 或字符串 完成它。调用析构函数来处理 BLOB 或 即使对绑定 API 的调用失败,也是字符串。如果第五个参数是 特殊值 SQLITE_STATIC,则 SQLite 假定 信息位于静态、非托管空间中,不需要 释放。如果第五个参数的值为 SQLITE_TRANSIENT,则 SQLite 立即创建自己的私有数据副本,在 sqlite3_bind_*() 例程返回。

因此,使用其原生 API 将 QString 与 sqlite 一起使用的正确方法是:

// Create and evaluate a statement
sqlite3_stmt *statement;
// Note: return value should be checked for error here
sqlite3_prepare_v2(pointer_to_sqlite_db, sql.toUtf8().constData(), sql.length() + 1, &statement, NULL);
// Bind QString to SQL statement - it must be transient so that sql text persist even after function call is over and data is destructed
sqlite3_bind_text(statement, current_parameter++, text.toUtf8().constData(), -1, SQLITE_TRANSIENT);
// step
sqlite3_step(statement);
// clean up
sqlite3_finalize(statement);

如何将const char * 转换回QString

static QString StringFromUnsignedChar( const unsigned char *str )

    return QString::fromUtf8(reinterpret_cast<const char*>(str));


QString result = StringFromUnsignedChar(sqlite3_column_text(statement, column));

【讨论】:

不需要到 std::string 的转换,如果你确实想要它,你可以使用 QString::fromStdstring

以上是关于如何使用本机 sqlite 库而不是 QSql 从 sqlite 插入和检索 QString的主要内容,如果未能解决你的问题,请参考以下文章

如何使用后期绑定调用本机 C 库?

如何在从cordova创建的本机代码中使用相同的android SQLite DB?

使用音频单元(混音器主机)从 iPod 库而不是预先选择的声音文件中播放。

CMake:是不是可以仅从静态库而不是源代码构建可执行文件?

如何强制 Maven 使用我的本地存储库而不是去远程存储库检索工件?

我正在尝试从 qSQL 数据库中检索数据