不累的王
普通会员
发贴: 339
积分: 0
来自:
注册日期: 2006-05-26
发表时间: 2008-04-10 02:26:40
--------------------------------------------------------------------------------
yy使用C语言为Python编写纠错码模块
去年一个项目的开发中因为需要使用到纠错码,所以自然而然的接触到了rscode。
rscode 是Reed-Solomon纠错算法的一种实现,被广泛的使用于Audio CD和CD-ROM。关于rscode更详细
的描述,请前往其官方网站查看(http://rscode.sourceforge.net/)。
rscode 使用标准C编写,优点自不必说,跨平台、执行效率高。可C代码也有其固有的麻烦,每次调试都
需要编译,开发过程远不如脚本语言来得阳春白雪。于是乎,笔者生出一个想法:何不将其编译为Python模
块?这样在测试程序逻辑的时候可以省去不少的麻烦。现在将过程记录如下,希望对大家有所帮助。
首先在官方网站下载源文件。
解压源代码,并将其编译为静态链接库。
项目的环境为Win32,不过因为笔者的拧巴性格,还是使用了MingGW系列的工具对其进行编译。
执行make,得到静态链接库:
libecc.a
接下来开始为我们的模块编写C源码。
/*
* RsCode.c
*/
#include <python.h>
#include "ecc.h"
//编码部分
//包裹函数调用的实际编码函数
//参数依次为:原文buffer指针,buffer长度,得到的rscode编码串的指针
void _encode(char* data, unsigned long size, char* codeword)
{
//调用rscode库提供的两个函数,分别完成初始化和编码工作。
initialize_ecc();
encode_data(data, size, codeword);
}
//编码部分的包裹函数,这个函数将和CPython的虚拟机进行亲密接触 :)
//函数参数的定义。self为当前模块的引用;args为传入这个函数的参数,可以是任意数据类型。当
然,如果传入参数与处理过程不匹配的话,就会产生一个异常。
PyObject* wrap_encode(PyObject* self, PyObject* args)
{
char* data;
int data_size;
char codeword[256];
PyObject* resString;//储存编码结果的Python字符串对象。
//使用Python/C API中Tuple对象的方法,得到指定顺序的参数。
//在这个函数中,我们将参数视为一个列表,其中第一个元素为数据的引用,第二个元素为数据
长度。
data = PyString_AsString(PyTuple_GetItem(args, 0));
data_size = PyInt_AsLong(PyTuple_GetItem(args, 1));
//调用前面定义的编码函数,将结果存入codeword中。
_encode(data, data_size, codeword);
//构造Python中的Buffer对象。
//PyString_FromStringAndSize这个函数是笔者从Python/C API的几角旮旯中找到的,它将严格
按照输入数据的指针和长度构造PythonBuffer对象,而不会像一般的PythonString对象构造API一样遇到“\n”就
结束了。这函数恐怕是大家最需要的了,呵呵。
resString = PyString_FromStringAndSize(codeword, data_size + NPAR);
//OK这个函数最终将返回生成的Python Buffer对象。
return resString;
}
//解码部分
//格式方面照猫画虎即可,解码时调用的rscode库中的方法,参照了rscode自带的例子。
void _decode(char* data, unsigned long data_size)
{
initialize_ecc();
decode_data(data, data_size);
correct_errors_erasures(data, data_size, 0, 0);
}
//同样需要编写一个返回Python数据类型的包裹函数。
PyObject* wrap_decode(PyObject* self, PyObject* args)
{
char* data;
int data_size;
PyObject* resString;
data = PyString_AsString(PyTuple_GetItem(args, 0));
data_size = PyInt_AsLong(PyTuple_GetItem(args, 1));
_decode(data, data_size);
resString = PyString_FromStringAndSize(data, data_size);
return resString;
}
//定义这个模块所包含的方法,格式如下的
static PyMethodDef RsCodeMethods[] =
{
//分别声明方法在Python中的名字,实际执行中调用的C包裹函数名,以及Python函数说明文
档。
{"encode", wrap_encode, METH_VARARGS,
"encode(str, str_size)\n decode data with rscode.\nlen(data) + NPAR < 256\n"
},
{"decode", wrap_decode, METH_VARARGS,
"decode(codeword, codeword_size)\n decode data with rscode.\n"
},
//以两个NULL作为声明的结束。
{NULL, NULL}
};
//模块在被引用时,将调用的初始化函数。作用是向虚拟机注册模块中的对象。注意函数名称的格式。
void initRsCode()
{
PyObject* m;
m = Py_InitModule("RsCode", RsCodeMethods);
}
/*
* End Of RsCode.c
*/
下面完成RsCode.c的编译。
#
# Makefile for RsCode.pyd
#
# ecc.h 所在的目录。
RSHEAD_FLAG = -I"d:\ts\sm\rscode-1.0"
# libecc.a 路径。
RS_LIB = d:\ts\sm\rscode-1.0\libecc.a
# Python头文件所在位置
PYHEAD_FLAG = -I"C:\Python24\include"
# Python虚拟机静态库路径
PY_LIB = c:\Python24\libs\libpython24.a #
# 我们的目标文件
TARGETS = RsCode.pyd
all:
gcc $(RSHEAD_FLAG) $(RS_LI $(PYHEAD_FLAG) $(PY_LI -shared -o $(TARGETS)
# End of Makefile
从Makefile不难看出,我们编译后得到的模块RsCode.pyd其实是一个动态链接库。
最后,让我们来看看如何使用这个刚刚得到的模块。
首先保证RsCode.pyd在Python安装目录下的Lib目录下,或是和调用这个模块的Python脚本在同一个目
录,以保证Python能够找到这个模块。
首先,通过import关键字加载这个模块。
>>> import RsCode
看看里面有什么?是不是我们定义的两个Python函数?
>>> dir(RsCode)
['__doc__', '__file__', '__name__', 'decode', 'encode']
看看函数的帮助文档怎么说?
>>> help(RsCode.decode)
Help on built-in function decode in module RsCode:
decode(...)
decode(codeword, codeword_size)
decode data with rscode.
定义一个字符串Buffer
>>> src = 'hello, world'
用rscode进行纠错编码。
>>> codeword = RsCode.encode(src, len(src))
看,末尾的4个字节就是rscode加上的监督位。
>>> codeword
'hello, world\xeb\t\xa1\xe7'
(注: rscode的纠错能力与监督位的长短有关,对于相同长度信息,监督位越多,纠错效果越好。监督位
长度 NPAR 可以在rscode的源码中找到,修改它的值再编译即可得到拥有相应长度监督位的rscode库。默认状态
下监督位长为4。CD-ROM的rscode使用24/28的rscode,即28字节长度的编码中含有24位有效信息及4位监督位。)
让我们制造一些错误,用字符'z'替换掉字符'r'。
>>> error = codeword.replace('r', 'z')
原文中的"world"变成了"wozld"
>>> error
'hello, wozld\xeb\t\xa1\xe7'
使用解码函数进行纠错。
>>> RsCode.decode(error, len(error))
'hello, world\xeb\t\xa1\xe7'
因为,解码函数修改了那个包含错误的buffer,所以字符串error的值也改变了。
最终,error中储存内容为原来的正确内容加上监督位。
>>> error
'hello, world\xeb\t\xa1\xe7'
OK,一切正常。
以上就是笔者编译这个模块的全部过程,语焉不详之处,请查阅参考文献。
通过使用这个模块,俺顺利地使用Python完成了程序的原型设计。俺觉得,这要比在Python中使用
os.system()函数调用外部程序的dirty方法优雅多了。常言道“磨刀不误砍柴功”,如果在其他项目的编写中也使
用这种方法,应该可以大大减少开发中的工作量,如果引擎也能提供一个Python模块……浮想联翩中……
故事讲完了,希望本文为大家在新的春天里的工作有所帮助,另外也希望笔者能够骗到多多稿费!
参考文献:
Extending and Embedding the Python Interpreter
http://docs.python.org/ext/ext.html
Python/C API Reference Manual
http://docs.python.org/api/api.html
ps1:
标题中的yy表示原创
ps2:
郁闷,没赶上这期的炖排……
等下期出来的时候就得承受由CPI上涨造成的货币贬值了,囧TL
<---- 以上言论仅代表本人立场 ---->
__________________
“……机器人罗诺,现在我要对你下达最新指令了。”
“是,主人。我都等了三万年了呢。”
“你还记得回地球的航路吧?” |
|