Cython调用getline出现未解析外部符号问题求助
问题
尝试用Cython逐行读取二进制文件,编译时出现错误,推测是getline导致的。测试代码如下:
# test_getline_cython.pyx cimport cython from libc.stdio cimport FILE, fopen, fclose, getline def load_binary(bytes file_name): cdef: Py_ssize_t h = 0 FILE * cfile = fopen(file_name, "rb") size_t ll = 0 ssize_t bytes_read char * buffer = NULL list contents = [None] * 2 while True: bytes_read = getline(&buffer, &ll, cfile) if bytes_read < 0: break contents[h] = buffer[:bytes_read].decode() h += 1 fclose(cfile) return contents
使用命令cythonize -a -i test_getline_cython.pyx编译后,出现如下错误:
Compiling E:\test_proteins\test_getline_cython.pyx because it changed. [1/1] Cythonizing E:\test_proteins\test_getline_cython.pyx test_getline_cython.c E:\test_proteins\test_getline_cython.c(2530): warning C4013: 'getline' undefined; assuming extern returning int Creating library E:\test_proteins\tmpjnrzzce8\Release\test_proteins\test_getline_cython.cp312-win_amd64.lib and object E:\test_proteins\tmpjnrzzce8\Release\test_proteins\test_getline_cython.cp312-win_amd64.exp test_getline_cython.obj : error LNK2001: unresolved external symbol getline build\lib.win-amd64-cpython-312\test_getline_cython.cp312-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals error: command 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\bin\HostX86\x64\link.exe' failed with exit code 1120
getline已在Cython的libc.stdio中声明,但编译报错,求解决方法。
解决方案
问题根源是Windows平台的MSVC标准库不支持POSIX的getline函数,Cython虽然在libc.stdio中声明了这个函数,但实际链接时找不到对应的实现。可以通过以下两种方式解决:
方法1:替换为Windows兼容的逐行读取函数
放弃getline,改用Windows标准库中的fgets函数,修改后的代码如下:
# test_getline_cython.pyx cimport cython from libc.stdio cimport FILE, fopen, fclose, fgets from libc.string cimport strlen def load_binary(bytes file_name): cdef: Py_ssize_t h = 0 FILE * cfile = fopen(file_name, "rb") char buffer[1024] # 定义固定大小的缓冲区,可根据需求调整 list contents = [] # 改为动态列表,避免固定长度限制 if not cfile: raise IOError(f"无法打开文件: {file_name.decode()}") while True: if fgets(buffer, sizeof(buffer), cfile) is NULL: break # 去掉fgets读取的换行符(如果存在) cdef size_t len = strlen(buffer) if len > 0 and buffer[len-1] == '\n': buffer[len-1] = '\0' len -= 1 contents.append(buffer[:len].decode()) h += 1 fclose(cfile) return contents
方法2:手动实现getline函数
如果坚持要使用getline的动态缓冲区行为,可以自己实现一个兼容版本,添加到Cython代码中:
# test_getline_cython.pyx cimport cython from libc.stdio cimport FILE, fopen, fclose, fgetc from libc.stdlib cimport malloc, realloc, free from libc.string cimport memcpy # 手动实现getline函数 cdef ssize_t my_getline(char **lineptr, size_t *n, FILE *stream): cdef: ssize_t pos = 0 int c size_t buf_size = *n if *n > 0 else 128 # 默认初始缓冲区大小 if *lineptr is NULL: *lineptr = <char*>malloc(buf_size) if *lineptr is NULL: return -1 while True: c = fgetc(stream) if c == EOF: break # 缓冲区满了就扩容 if pos >= buf_size - 1: buf_size *= 2 cdef char *new_buf = <char*>realloc(*lineptr, buf_size) if new_buf is NULL: return -1 *lineptr = new_buf (*lineptr)[pos] = <char>c pos += 1 if c == '\n': break if pos == 0 and c == EOF: return -1 (*lineptr)[pos] = '\0' *n = buf_size return pos def load_binary(bytes file_name): cdef: Py_ssize_t h = 0 FILE * cfile = fopen(file_name, "rb") size_t ll = 0 ssize_t bytes_read char * buffer = NULL list contents = [] if not cfile: raise IOError(f"无法打开文件: {file_name.decode()}") while True: bytes_read = my_getline(&buffer, &ll, cfile) if bytes_read < 0: break contents.append(buffer[:bytes_read].decode()) h += 1 if buffer is not NULL: free(buffer) # 手动释放内存,避免泄漏 fclose(cfile) return contents
额外注意事项
- 原代码中
contents = [None] * 2是固定长度列表,实际读取行数超过2会报错,建议改为动态列表contents = []并使用append添加元素。 - 打开文件后要检查
cfile是否为NULL,避免空指针访问导致程序崩溃。 - 使用手动分配的内存时,记得在最后释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Elkan
相关产品推荐
相关产品推荐

