使用fstream::read读取SQLite脚本出现无效字符的问题及解决
SQLite3脚本读取末尾出现无效字符的问题排查与解决
问题根源
你碰到的这个问题其实是C风格字符串的常见坑——SQLite的sqlite3_exec函数要求传入的SQL脚本是一个以\0(空字符)结尾的C风格字符串,但你的代码里只把文件内容原封不动读到了buffer中,没有添加这个终止符。
具体来说:
- 你通过
seekg和tellg获取了文件的字节长度,然后分配了对应大小的char数组作为buffer - 用
read把文件内容读入buffer后,buffer的最后一个字符后面没有\0 - 当
std::cout输出buffer,或者sqlite3_exec解析脚本时,它们会一直读取内存直到遇到第一个\0,这就会把buffer后面的随机内存数据(也就是你看到的空格、数字、字母等无效字符)当成脚本的一部分,最终触发SQL语法错误。
另外提一句,你代码里的delete buffer应该改成delete[] buffer(因为是用new[]分配的数组),虽然这不是当前问题的原因,但不修正可能会导致未定义行为。
解决方法
这里有两种靠谱的解决思路,推荐第二种更符合现代C++的写法:
方法一:手动添加字符串终止符
给buffer多分配一个字节的空间,在读取完文件内容后手动添加\0:
std::cerr << "Creating database if needed...\n"; const char* sql_script = "/path/to/script.sql"; int script_length; bool result = false; std::ifstream script_fs(sql_script, std::fstream::binary | std::fstream::in); if (script_fs) { char* buffer; char** err_msg = NULL; script_fs.seekg(0, script_fs.end); script_length = script_fs.tellg(); script_fs.seekg(0, script_fs.beg); // 多分配1个字节用于存储终止符 buffer = new char[script_length + 1]; script_fs.read(buffer, script_length); // 在脚本内容末尾添加空字符 buffer[script_length] = '\0'; std::cout << "sql:\n" << buffer << "\n"; if (sqlite3_exec(m_db, buffer, NULL, NULL, err_msg) == SQLITE_OK){ result = true; } else { std::cerr << "Error executing: " << sqlite3_errmsg(m_db) << "\n" << err_msg << "\n"; } // 用delete[]对应new[] delete[] buffer; script_fs.close(); } else { std::cerr << "Error opening script: " << strerror(errno) << "\n"; } return result;
方法二:改用std::string存储脚本内容
C++的std::string会自动管理字符串的终止符,而且不用手动处理内存分配,代码更简洁安全:
std::cerr << "Creating database if needed...\n"; const char* sql_script = "/path/to/script.sql"; bool result = false; std::ifstream script_fs(sql_script, std::fstream::binary | std::fstream::in); if (script_fs) { char** err_msg = NULL; // 直接将文件内容读取到std::string中 std::string buffer(std::istreambuf_iterator<char>(script_fs), std::istreambuf_iterator<char>()); std::cout << "sql:\n" << buffer << "\n"; // 用c_str()获取C风格的终止符结尾字符串 if (sqlite3_exec(m_db, buffer.c_str(), NULL, NULL, err_msg) == SQLITE_OK){ result = true; } else { std::cerr << "Error executing: " << sqlite3_errmsg(m_db) << "\n" << err_msg << "\n"; } script_fs.close(); } else { std::cerr << "Error opening script: " << strerror(errno) << "\n"; } return result;
内容的提问来源于stack exchange,提问作者user96649
相关产品推荐
相关产品推荐

