15.19 从C语言中读取类文件对象

问题

你要写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扩展的时候,能简单就尽量简单。

艾伯特(http://www.aibbt.com/)国内第一家人工智能门户

Python Cookbook(第3版)中文版:15.19 从C语言中读取类文件对象的更多相关文章

  1. Python Cookbook(第3版)中文版:15.20 处理C语言中的可迭代对象

    15.20 处理C语言中的可迭代对象¶ 问题¶ 你想写C扩展代码处理来自任何可迭代对象如列表.元组.文件或生成器中的元素. 解决方案¶ 下面是一个C扩展函数例子,演示了怎样处理可迭代对象中的元素: s ...

  2. Python Cookbook(第3版)中文版:15.21 诊断分段错误

    15.21 诊断分段错误¶ 问题¶ 解释器因为某个分段错误.总线错误.访问越界或其他致命错误而突然间奔溃. 你想获得Python堆栈信息,从而找出在发生错误的时候你的程序运行点. 解决方案¶ faul ...

  3. Python Cookbook(第3版)中文版:15.18 传递已打开的文件给C扩展

    15.18 传递已打开的文件给C扩展¶ 问题¶ 你在Python中有一个打开的文件对象,但是需要将它传给要使用这个文件的C扩展. 解决方案¶ 要将一个文件转换为一个整型的文件描述符,使用 PyFile ...

  4. Python Cookbook(第3版) 中文版 pdf完整版|网盘下载内附提取码

    Python Cookbook(第3版)中文版介绍了Python应用在各个领域中的一些使用技巧和方法,其主题涵盖了数据结构和算法,字符串和文本,数字.日期和时间,迭代器和生成器,文件和I/O,数据编码 ...

  5. Python Cookbook(第3版)中文版:15.14 传递Unicode字符串给C函数库

    15.14 传递Unicode字符串给C函数库¶ 问题¶ 你要写一个扩展模块,需要将一个Python字符串传递给C的某个库函数,但是这个函数不知道该怎么处理Unicode. 解决方案¶ 这里我们需要考 ...

  6. Python Cookbook(第3版)中文版:15.15 C字符串转换为Python字符串

    15.15 C字符串转换为Python字符串¶ 问题¶ 怎样将C中的字符串转换为Python字节或一个字符串对象? 解决方案¶ C字符串使用一对 char * 和 int 来表示, 你需要决定字符串到 ...

  7. Python Cookbook(第3版)中文版:15.16 不确定编码格式的C字符串

    15.16 不确定编码格式的C字符串¶ 问题¶ 你要在C和Python直接来回转换字符串,但是C中的编码格式并不确定. 例如,可能C中的数据期望是UTF-8,但是并没有强制它必须是. 你想编写代码来以 ...

  8. Python Cookbook(第3版)中文版:15.17 传递文件名给C扩展

    15.17 传递文件名给C扩展¶ 问题¶ 你需要向C库函数传递文件名,但是需要确保文件名根据系统期望的文件名编码方式编码过. 解决方案¶ 写一个接受一个文件名为参数的扩展函数,如下这样: static ...

  9. 实操一下<python cookbook>第三版1

    这几天没写代码, 练一下代码. 找的书是<python cookbook>第三版的电子书. *这个操作符,运用得好,确实少很多代码,且清晰易懂. p = (4, 5) x, y = p p ...

随机推荐

  1. 小甲鱼OD学习第6讲

    这次我们的任务是破解这个通讯录的软件,首先,我们在通讯录一个分组添加第5个人,发现弹出对话框,限制每组只能添加4个人 并且发现最多只能添加3个分组 我们把程序载入OD,运行,添加人,这个时候点击暂停, ...

  2. ionic2 +Angular 使用自定义图标

    结合阿里巴巴矢量图标库实现在ionic2开发中使用自定义图标. step1:在阿里巴巴图标管理中新建项目,并添加自己选中的图标到购物车: step2:将购物车中的图标"添加至项目" ...

  3. 使用CentOS Linux Bridge搭建Vxlan环境

    一. 基础环境使用VmWare虚拟两台Linux机器.CentOS 7,Linux内核如下:4.5.3-1.el7.elrepo.x86_64如果内核版本太低,是不支持VxLan的.可以使用一下命令进 ...

  4. Project support for both iOS 6 and iOS 7

    原文:https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TransitionGuide/S ...

  5. PowerDesigner 简单应用(转载)

    PowerDesigner是一款功能非常强大的建模工具软件,足以与Rose比肩,同样是当今最著名的建模软件之一.Rose是专攻UML对象模型的建模工具,之后才向数据库建模发展,而PowerDesign ...

  6. 初识vue——起步

    一.目录结构: 我们经常使用的是以下几个目录: 1.assets:静态资产文件:在vue组件中,所有组件中,所有模板和CSS都会被vue-html-loader和css-loader解析,并查找资源u ...

  7. 使用 neon-wallet-db + neon-js + NEO-cli /rpc 搭建轻钱包服务端

    本文将搭建一个不具有任何功能的NEO轻钱包,所有的精力都仅集中于成功运行neon-wallet-db项目并搭配全节点的neo-cli /rpc接口为轻钱包客户端提供服务. 首先需要准备几个项目: ne ...

  8. basler 相机拍照简单类综合Emgu.CV---得到图档

    在网上找了半天都是下载要钱,自己试做了,经测试能ok,一起分享吧.给初学的人一点鼓励. using System;using System.Collections.Generic;using Syst ...

  9. ThreadLoacl,InheritableThreadLocal,原理,以及配合线程池使用的一些坑

    虽然使用AOP可以获取方法签名,但是如果要获取方法中计算得出的数据,那么就得使用ThreadLocal,如果还涉及父线程,那么可以选择InheritableThreadLocal. 注意:理解一些原理 ...

  10. 关于使用srping @RequestParam 容易出错的地方

    大家都知道,在spring中的@RequestParam主要用户传递参数用的,具体的解释就是将请求参数去数据映射到功能处理方法的参数上.其中包括三个参数: value:参数名字,即入参的请求参数名字, ...