Lectura de objetos similares a archivos de Python desde C | Python

Escribir código de extensión C que consume datos de cualquier objeto similar a un archivo de Python (por ejemplo, archivos normales, objetos StringIO, etc.). read()El método debe invocarse repetidamente para consumir datos en un objeto similar a un archivo y tomar medidas para decodificar correctamente los datos resultantes.
A continuación se muestra una función de extensión C que simplemente consume todos los datos en un objeto similar a un archivo y los vuelca a la salida estándar.

Código #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;
}

Se prepara un objeto similar a un archivo, como una instancia de StringIO , para probar el código y luego se pasa:

Código #2:

import io
f = io.StringIO('Hello\nWorld\n')
import sample
sample.consume_file(f)

Producción :

Hello
World

A diferencia de un archivo de sistema normal, un objeto similar a un archivo no se crea necesariamente en torno a un descriptor de archivo de bajo nivel. Por lo tanto, las funciones normales de la biblioteca C no se pueden usar para acceder a ella. En su lugar, se utiliza una API C de Python para manipular el objeto similar a un archivo como lo haría en Python.
Entonces, el read()método se extrae del objeto pasado. Se crea una lista de argumentos y luego se pasa repetidamente a PyObject_Call()para invocar el método. Para detectar el final del archivo (EOF), PySequence_Length()se usa para ver si el resultado devuelto tiene una longitud cero.
Para todas las operaciones de E/S, la preocupación es la codificación subyacente y la distinción entre bytes y Unicode. Esta receta muestra cómo leer un archivo en modo de texto y decodificar el texto resultante en una codificación de bytes que puede usar C. Si el archivo se lee en modo binario, solo se realizarán cambios menores como se muestra en el código a continuación.

Código #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);

Publicación traducida automáticamente

Artículo escrito por manikachandna97 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *