📜  从 C 中读取Python类文件对象 | Python

📅  最后修改于: 2022-05-13 01:54:34.779000             🧑  作者: Mango

从 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);