使用ctypes调用SQLite动态库时未打开现有库却新建文件的问题
问题原因
- 字符串编码不匹配:SQLite的
sqlite3_openC函数接收的是UTF-8编码的const char*类型参数,但直接传递Python字符串时,macOS下的ctypes会默认将其转换为宽字符(wchar_t*)。SQLite无法正确解析宽字符,会把第一个字节后的空字节当成字符串结束符,因此只识别了路径的第一个字符(如D),并以此创建新文件。 - 相对路径解析错误:脚本执行时的工作目录(CWD)并非
MyProject/SRC,导致DATA/imdb-tiny.db这个相对路径被解析到了错误的位置,再结合编码问题,最终生成了单字符命名的空数据库文件。
解决方法
1. 传递正确编码的路径字符串
将Python字符串转换为UTF-8编码的字节串,确保ctypes传递的是const char*类型:
import ctypes # 加载SQLite动态库 sqlite3 = ctypes.CDLL("/usr/lib/libsqlite3.0.dylib") # 定义sqlite3_open的函数原型 sqlite3.sqlite3_open.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] sqlite3.sqlite3_open.restype = ctypes.c_int # 转换路径为UTF-8字节串 db_path = "DATA/imdb-tiny.db".encode('utf-8') db_handle = ctypes.c_void_p() # 调用打开函数 result = sqlite3.sqlite3_open(db_path, ctypes.byref(db_handle)) if result != 0: print(f"打开数据库失败,错误码:{result}")
2. 使用绝对路径避免工作目录问题
通过脚本所在目录拼接出数据库的绝对路径,彻底解决相对路径解析错误:
import ctypes import os # 获取当前脚本所在目录 script_dir = os.path.dirname(os.path.abspath(__file__)) # 拼接数据库绝对路径 db_abs_path = os.path.join(script_dir, "DATA", "imdb-tiny.db") # 转换为UTF-8字节串 db_path = db_abs_path.encode('utf-8') sqlite3 = ctypes.CDLL("/usr/lib/libsqlite3.0.dylib") sqlite3.sqlite3_open.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] sqlite3.sqlite3_open.restype = ctypes.c_int db_handle = ctypes.c_void_p() result = sqlite3.sqlite3_open(db_path, ctypes.byref(db_handle)) if result != 0: print(f"打开数据库失败,错误码:{result}")
3. 推荐使用sqlite3_open_v2增强控制
sqlite3_open_v2支持指定打开模式,比如要求必须打开现有数据库(不存在则报错,不会创建新文件),进一步避免意外创建空文件:
import ctypes import os # 定义SQLite打开模式常量 SQLITE_OPEN_READWRITE = 0x00000002 SQLITE_OPEN_EXISTING = 0x00000020 script_dir = os.path.dirname(os.path.abspath(__file__)) db_abs_path = os.path.join(script_dir, "DATA", "imdb-tiny.db") db_path = db_abs_path.encode('utf-8') sqlite3 = ctypes.CDLL("/usr/lib/libsqlite3.0.dylib") # 定义sqlite3_open_v2的函数原型 sqlite3.sqlite3_open_v2.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_char_p] sqlite3.sqlite3_open_v2.restype = ctypes.c_int db_handle = ctypes.c_void_p() # 调用打开函数,指定模式为读写+必须存在 result = sqlite3.sqlite3_open_v2(db_path, ctypes.byref(db_handle), SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXISTING, None) if result != 0: # 获取错误信息 err_msg = ctypes.c_char_p() sqlite3.sqlite3_errmsg(db_handle, ctypes.byref(err_msg)) print(f"打开数据库失败:{err_msg.decode('utf-8')}") sqlite3.sqlite3_close(db_handle)
内容的提问来源于stack exchange,提问作者Magyar_57
相关产品推荐
相关产品推荐

