为啥我无法打开/读取从 Python 调用的 C 扩展名中的 txt 文件?
Posted
技术标签:
【中文标题】为啥我无法打开/读取从 Python 调用的 C 扩展名中的 txt 文件?【英文标题】:Why I'm unable to open/read txt file in C extension called from Python?为什么我无法打开/读取从 Python 调用的 C 扩展名中的 txt 文件? 【发布时间】:2020-06-18 11:38:57 【问题描述】:我尝试将文件名从 python 传递给 C\C++ .dll 并让 C\C++ 代码打开/读取该文件。
这是我尝试使用 ctypes 实现的一个非常简单的示例。请原谅我的 C++ 代码的菜鸟。我在这里学习。
//C++ code in example.cpp compiled to example.dll
#include <stdio.h>
extern "C"
char* _line;
char* test(char* txt)
FILE* f_name;
f_name = fopen(txt, "r");
//... read data from file and do something
_line = txt;
return _line; // this is just my sanity check to see if "file.txt" got passed from python
我使用cygwin-64编译成.dll:
g++ -fPIC -shared example.cpp -o example.dll
Python 包装器:
import ctypes
print("works!") #just sanity check...
lib = ctypes.CDLL("example.dll")
cpp_file_access = lib.test
cpp_file_access.argtypes = [ctypes.c_char_p]
cpp_file_access.restype = ctypes.c_char_p
print(cpp_file_access(b"file.txt")) #sanity check to see if file name got passed
当从 python 调用 .dll 中的 test() 时,程序不会越过行:
f_name = fopen(txt, "r");
无论我使用哪种方法打开/访问所述 txt 文件。它要么“挂起”并且什么都不做,要么抛出 WindowsError 指示访问冲突 0x00000(...)。 当我在 C++ 代码中使用任何类型的“打印”函数时,也会发生同样的行为,例如:
printf("test print !");
std::cout << "another test print";
.dll 代码在其他情况下正常工作,除非我试图打开/读取 txt 文件或在从 python 调用的 C++ 代码中将某些内容“打印”到控制台。 实际上,当我将其编译为 .exe 文件时,它会打开/读取文件并将任何内容打印到控制台没有问题。
我找不到我的问题的任何解决方案/答案。 也许这里有人可以告诉我我做错了什么?
[编辑#1] 我注意到了一些东西。 当我尝试在 Python3.8 中运行此代码时,我收到错误消息文件:
cygwin1.dll
cygstdc++-6.dll
cyggcc_s-seh-1.dll
即使它们位于 cygwin 的 bin 文件夹中并且 env 路径设置正确,也无法定位。我必须将这些文件复制粘贴到 example.dll 和 python 包装器(wrapper.py)所在的文件夹中。 我不知道这是否与我的问题有关,但我认为值得一提。我开始对那个 cygwin 编译器非常怀疑。挖掘继续。
[编辑#2] 我通过添加 void test_2() 修改了 C++ 代码,因此在 python 和 .dll 之间没有传递参数。
__declspec(dllexport) void test_2()
FILE* f_name;
f_name = fopen("file.txt", "r");
并从 python 调用它:
void_acc = lib.test_2
void_acc.restype = None
void_acc()
print ("program ended, go away !") // .. sanity check
问题依旧。程序执行到 fopen() 行时停止。
【问题讨论】:
【参考方案1】:两件事一目了然:
您必须确保在函数声明之前使用 __declspec(dllexport) 或通过将其包含在 def 文件中来导出函数。 (如果您到达 fopen 行,这不是问题 - 只是为了完整性而提及。) 您在 Python 中指定了宽 c 字符串指针 (c_wchar_t),但在 c 中声明了窄 c 字符串 (char*),这些需要匹配。【讨论】:
是的。谢谢你的建议。至于 fopen() 行。如果我将其注释掉,程序将正确执行 - 在 python 控制台中打印“file.txt”。这就是为什么我询问打开/读取文件的原因,因为这显然是我的问题(或其他问题)。 我仍然认为字符串可能是问题所在,因为它将指针返回到同一个字符串,所以它看起来没有变化,因此返回不会受到影响。试试把std::out << txt;
;在 fopen
之前或代替 fopen
你得到“file.txt”还是“f”?
我添加了内联 std::cout<<txt;
它导致:WindowsError: exception: access violation reading 0xFFFFFFFFFFFFF
指向 wrapper.py 中的行:print(cpp_file_access(b"file.txt"))
@Rad226 两个错误就是一个正确。将 wchar* 传递给 char* 并将 char* 作为 wchar* 传回而不使用它似乎可以工作,但 fopen 不会喜欢它。
@Mark Tolonen 好的。但是我在我的 C++ 代码(上面的编辑#2)中添加了 void,我所做的只是fopen("file.txt", "r');
,但它仍然不起作用。 “file.txt”作为 fopen() 的参数,在这种情况下不是从 python 传递的,不正确吗? std::cout<<"some text";
in void 也会中断。【参考方案2】:
.argtypes
和 .restype
不正确。 C代码使用char*
所以使用c_char_p
:
import ctypes
lib = ctypes.CDLL("example.dll")
cpp_file_access = lib.test
cpp_file_access.argtypes = ctypes.c_char_p,
cpp_file_access.restype = ctypes.c_char_p
print(cpp_file_access(b'file.txt')) # pass byte string
请注意,您不需要将参数包装在 ctypes
类型中。 ctypes
已经知道来自 .argtypes
的类型,如果您没有传递与声明的类型兼容的 Python 类型,则会抱怨。
【讨论】:
对于 python2(不是 3),只要 fopen() 行被注释掉,它就可以正常工作而没有抱怨。仍然非常感谢您的建议。 @Rad226 创建一个minimal reproducible example。对于 Python 2 和 3,这是调用带有签名char* test(char* txt)
的函数的正确代码,因此问题出在您未显示的代码中。你得到什么错误?
没有错误并且程序执行在控制台中“挂起”,就像它正在等待输入一样,或者当从 C++ 代码执行任何类型的“打印”时,如 sdt::cout<<"thing";
我收到此错误:@ 987654332@。我没有显示的其他代码无关紧要。我将问题的根源确定为从 C++ 代码打开/读取文件或打印到控制台。没有这些一切都可以完美运行。【参考方案3】:
已解决。
毕竟是编译器问题。 我应该使用 mingw-64 位而不是使用 cygwin。
https://www.msys2.org/
我按照这个 yt 视频中的说明进行操作: https://www.youtube.com/watch?v=aXF4A5UeSeM
工作正常。
【讨论】:
【参考方案4】:如果由于某种原因您的链接已断开或您不想观看视频,我会发布该视频中的步骤以妥善保管。
转到: https://www.msys2.org/ 并单击下载安装程序的按钮。就我而言,它是(未来可能会有所不同):
msys2-x86_64-20200602.exe
安装它。记住你安装它的地方。这很快就会派上用场。 安装后,启动它(或选择安装完成后自动启动)。将出现命令行窗口。在该命令行窗口中写入/粘贴:
pacman -Syu --disable-download-timeout
等待它完成。之后关闭窗口。
转到您选择的安装文件夹。启动msys2.exe
。将出现命令行窗口。再写或粘贴:
pacman -Syu --disable-download-timeout
...并等待它完成。保持命令行窗口打开。之后我们将使用它。
完成后,转到初始网页:https://www.msys2.org/ 并向下滚动,然后转到“浏览:包列表”。
在左侧转到“搜索”。
将"Search in"
设置为"base packages"
并在搜索框中输入"gcc"
。选择:
mingw-w64-gcc
然后,从"binary packages"
列表中选择:
mingw-w64-x86_64-gcc
寻找"Installation"
标签(它应该在页面的顶部)。那里有一个命令。复制/粘贴到终端:
pacman -S mingw-w64-x86_64-gcc --disable-download-timeout
(添加了"--disable-download-timeout"
)
等待它安装。保持命令行窗口打开。有 2 件事要做。
在命令行窗口中,重新粘贴上一个命令,但将最后一个字母从 "gcc"
更改为 "gdb"
或使用:
pacman -S mingw-w64-x86_64-gdb --disable-download-timeout
等待它安装。保持命令行窗口打开。 1件事去。
在命令行窗口中,重新粘贴上一个命令,但将最后一个字母从 "gdb"
更改为 "make"
或使用:
pacman -S mingw-w64-x86_64-make --disable-download-timeout
等待它安装。我们完成了安装工作。然而,还有一个步骤 - 系统路径变量。
转到您安装msys2-x86_64-20200602.exe
的文件夹,应该有一个"msys2.exe"
文件和一个"mingw64"
文件夹。转到那个文件夹。查找"bin"
文件夹,复制其路径。
转到系统变量。
右键单击您的计算机图标。转到高级系统设置。转到环境变量(在底部)。查找名为“path”的变量,对其进行编辑。放 ”;”最后,如果它还没有准备好。在“;”后面粘贴<rest of the path>/mingw64/bin
文件夹路径。
现在您可以在终端中使用 g++ 命令,例如:
g++ -fPIC -shared example.cpp -o example.dll
(用于制作 .dll 文件)。你已经完成了。
【讨论】:
以上是关于为啥我无法打开/读取从 Python 调用的 C 扩展名中的 txt 文件?的主要内容,如果未能解决你的问题,请参考以下文章
为啥打开Mastercam9.0 显示 无法读取 c:\mcam9\temp\db.ovf
Python打开了一个文件并正在读取它的数据,为啥即使我删除了这个文件,python仍然可以读取它的数据?