您好,登錄后才能下訂單哦!
這篇文章主要講解了從C語言中讀取Python類文件對象的方法,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
問題
你要寫C擴展來讀取來自任何Python類文件對象中的數據(比如普通文件、StringIO對象等)。
解決方案
要讀取一個類文件對象的數據,你需要重復調用 read() 方法,然后正確的解碼獲得的數據。
下面是一個C擴展函數例子,僅僅只是讀取一個類文件對象中的所有數據并將其輸出到標準輸出:
#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實例,然后傳遞進來:
>>> import io >>> f = io.StringIO('Hello\nWorld\n') >>> import sample >>> sample.consume_file(f) Hello World >>>
討論
和普通系統文件不同的是,一個類文件對象并不需要使用低級文件描述符來構建。 因此,你不能使用普通的C庫函數來訪問它。 你需要使用Python的C API來像普通文件類似的那樣操作類文件對象。
在我們的解決方案中,read()
方法從被傳遞的對象中提取出來。 一個參數列表被構建然后不斷的被傳給 PyObject_Call()
來調用這個方法。 要檢查文件末尾(EOF),使用了 PySequence_Length()
來查看是否返回對象長度為0.
對于所有的I/O操作,你需要關注底層的編碼格式,還有字節和Unicode之前的區別。 本節演示了如何以文本模式讀取一個文件并將結果文本解碼為一個字節編碼,這樣在C中就可以使用它了。 如果你想以二進制模式讀取文件,只需要修改一點點即可,例如:
... /* 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); ...
本節最難的地方在于如何進行正確的內存管理。 當處理 PyObject *
變量的時候,需要注意管理引用計數以及在不需要的變量的時候清理它們的值。 對 Py_DECREF()
的調用就是來做這個的。
本節代碼以一種通用方式編寫,因此他也能適用于其他的文件操作,比如寫文件。 例如,要寫數據,只需要獲取類文件對象的 write()
方法,將數據轉換為合適的Python對象 (字節或Unicode),然后調用該方法將輸入寫入到文件。
最后,盡管類文件對象通常還提供其他方法(比如readline(), read_info()), 我們最好只使用基本的 read()
和 write()
方法。 在寫C擴展的時候,能簡單就盡量簡單。
看完上述內容,是不是對從C語言中讀取Python類文件對象的方法有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。