从 C 中读取Python类文件对象 | Python
编写使用来自任何Python类文件对象(例如,普通文件、StringIO 对象等)的数据的 C 扩展代码。必须重复调用read()
方法以使用类文件对象上的数据并采取措施正确解码结果数据。
下面给出的是一个 C 扩展函数,它仅使用类文件对象上的所有数据并将其转储到标准输出。
代码#1:
#define CHUNK_SIZE 8192
/* Consume a "file-like" object and write bytes to stdout */
static PyObject* py_consume_file(PyObject* self, PyObject* args)
{
PyObject* obj;
PyObject* read_meth;
PyObject* result = NULL;
PyObject* read_args;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return NULL;
}
/* Get the read method of the passed object */
if ((read_meth = PyObject_GetAttrString(obj, "read")) == NULL) {
return NULL;
}
/* Build the argument list to read() */
read_args = Py_BuildValue("(i)", CHUNK_SIZE);
while (1) {
PyObject* data;
PyObject* enc_data;
char* buf;
Py_ssize_t len;
/* Call read() */
if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) {
goto final;
}
/* Check for EOF */
if (PySequence_Length(data) == 0) {
Py_DECREF(data);
break;
}
/* Encode Unicode as Bytes for C */
if ((enc_data = PyUnicode_AsEncodedString(data,
"utf-8", "strict")) == NULL) {
Py_DECREF(data);
goto final;
}
/* Extract underlying buffer data */
PyBytes_AsStringAndSize(enc_data, &buf, &len);
/* Write to stdout (replace with something more useful) */
write(1, buf, len);
/* Cleanup */
Py_DECREF(enc_data);
Py_DECREF(data);
}
result = Py_BuildValue("");
final:
/* Cleanup */
Py_DECREF(read_meth);
Py_DECREF(read_args);
return result;
}
准备一个类似文件的对象(例如StringIO实例)来测试代码,然后将其传入:
代码#2:
import io
f = io.StringIO('Hello\nWorld\n')
import sample
sample.consume_file(f)
输出 :
Hello
World
与普通系统文件不同,类文件对象不一定围绕低级文件描述符构建。因此,不能使用普通的 C 库函数来访问它。相反,Python 的 C API 用于像在Python中那样操作类似文件的对象。
因此,从传递的对象中提取了read()
方法。构建一个参数列表,然后重复传递给PyObject_Call()
以调用该方法。为了检测文件结尾(EOF), PySequence_Length()
用于查看返回的结果是否为零长度。
对于所有 I/O 操作,关注的是底层编码以及字节和 Unicode 之间的区别。这个秘籍展示了如何以文本模式读取文件并将生成的文本解码为 C 可以使用的字节编码。如果以二进制模式读取文件,则只会进行微小的更改,如下面的代码所示。
代码#3:
/* Call read() */
if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) {
goto final;
}
/* Check for EOF */
if (PySequence_Length(data) == 0) {
Py_DECREF(data);
break;
}
if (!PyBytes_Check(data)) {
Py_DECREF(data);
PyErr_SetString(PyExc_IOError, "File must be in binary mode");
goto final;
}
/* Extract underlying buffer data */
PyBytes_AsStringAndSize(data, &buf, &len);