ESP32操作SQLite调用sqlite3_column_text提取文本乱码如何解决
问题根因
乱码和文本前置长度字段无关,核心错误是资源释放顺序错误:
sqlite3_column_text返回的字符串指针生命周期完全绑定在对应的预编译语句(即代码中的res)上,调用sqlite3_finalize释放语句、sqlite3_close关闭数据库后,该指针指向的内存会被SQLite回收,成为野指针。- 现有代码先保存字符串指针,立刻释放语句、关闭数据库,后续
response->printf读取的是已经被回收覆写的无效内存,因此开头出现随机乱码,未被覆写的后半段文本会正常显示。
修复方法
调整操作顺序:所有对查询结果列的读取、输出、拷贝操作,必须放在sqlite3_finalize和sqlite3_close调用之前完成。
修正后的基础实现代码:
if (sqlite3_step(res) == SQLITE_ROW) { // 预编译语句未释放时,返回的指针有效 const unsigned char * Auth = sqlite3_column_text(res, 0); const unsigned char * Cite = sqlite3_column_text(res, 1); // 直接在资源释放前完成输出,无需额外处理 response->printf("%s\n%s",Auth,Cite); // 所有结果读取完成后再释放资源 sqlite3_finalize(res); sqlite3_close(db); }
如果需要在释放数据库资源后继续使用查询到的字符串,需要自行拷贝文本到独立管理的内存中,这时候才需要用到sqlite3_column_bytes获取文本准确长度:
if (sqlite3_step(res) == SQLITE_ROW) { const unsigned char * Auth = sqlite3_column_text(res, 0); const unsigned char * Cite = sqlite3_column_text(res, 1); // 获取文本字节长度(不含C字符串末尾的\0终止符) int authLen = sqlite3_column_bytes(res, 0); int citeLen = sqlite3_column_bytes(res, 1); // 分配独立缓冲区拷贝内容 char *authBuf = (char*)malloc(authLen + 1); char *citeBuf = (char*)malloc(citeLen + 1); memcpy(authBuf, Auth, authLen); authBuf[authLen] = '\0'; memcpy(citeBuf, Cite, citeLen); citeBuf[citeLen] = '\0'; // 拷贝完成后再释放数据库资源 sqlite3_finalize(res); sqlite3_close(db); // 后续可以独立使用拷贝后的缓冲区,用完记得释放内存 response->printf("%s\n%s",authBuf,citeBuf); free(authBuf); free(citeBuf); }
sqlite3_column_bytes使用说明 - 该接口返回对应列内容的实际字节长度,对于TEXT类型列,返回值不包含C字符串要求的末尾
\0终止符,仅作为内存拷贝的长度参数使用。 - 该接口同样需要在预编译语句释放前调用,否则返回值无效。
sqlite3_column_text返回的本身就是标准的以\0结尾的C字符串,不存在前置长度字段,只要指针指向的内存未被回收,直接用%s格式化输出不会出现问题。
内容的提问来源于stack exchange,提问作者Rossati
相关产品推荐
相关产品推荐

