为什么我无法打开/读取从 Python 调用的 C 扩展名中的 txt 文件?

Why I'm unable to open/read txt file in C extension called from Python?

本文关键字:调用 扩展名 文件 txt Python 读取 为什么      更新时间:2023-10-16

我尝试将文件名从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文件夹中并且环境路径设置正确,也无法找到。我不得不将这些文件复制粘贴到示例.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() 行时停止。

两件事一目了然:

  • 您必须确保在函数声明之前使用 __declspec(dllexport) 导出函数,或者将其包含在 def 文件中。 (如果你正在到达fopen线,这不是问题 - 只是为了完整性而提及。
  • 您在 Python 中指定了一个宽 c 字符串指针 (c_wchar_t),但在 c 中声明了窄 c 字符串 (char*),这些需要匹配。

.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 类型,则会抱怨。

解决了。

毕竟这是编译器问题。 与其使用cygwin,我应该使用mingw-64位。

https://www.msys2.org/

我按照这个 yt 视频中的说明进行操作: https://www.youtube.com/watch?v=aXF4A5UeSeM

工作正常。

如果由于某种原因yt链接已关闭,或者您不想看视频,我将发布该viodeo的步骤以进行保管。

转到: https://www.msys2.org/并单击下载安装程序的按钮。就我而言,它是(将来可能会有所不同):

msys2-x86_64-20200602.exe

安装它。记住你在哪里安装它。这将在几分钟内派上用场。 安装后,启动它(或选择在安装完成后自动启动)。将出现命令行窗口。在该命令行窗口中写入/pasete:

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文件)。大功告成。