Linux下的语料处理

用Linux处理语料(持续更新中)

自然语言的计算机处理一开始就是在类Unix系统中进行的,很多早期的NLP程序等等都是在Unix环境下开发的,所以大多可以看到源代码,研究者可以根据自身的需要对其进行修改完善,或者放到自己的程序中,来实现一些更高级的功能。

我们这里大多是英语老师或学生,外语出身,对技术方面掌握得可能不是很多,另外可能多数用的是Windows操作系统,习惯了使用带有图形界面(GUI)的软件,对命令行并不熟悉。但是,Windows下必须使用一些特定的软件才做得到的功能,在Linux下往往用发行版自带的命令行工具就可以完成,而且没有商业软件的各种限制(比如限制只能处理特定长度的文件等)。Linux下默认就安装了perl、bash等环境,而这些东西其实是实现文本处理的真正功臣,软件其实多半是在其基础上加入了图形界面而已。这里列出的是几个最基本的小工具的用法。我是当英语老师的,出于教学科研需要在这个优秀的论坛里学到了许多宝贵的知识和技能,以此作为一点回报。此处抛砖引玉,说得不正确之处请各位高手指正并补充。


文件处理:

转换文本文件的编码
enca filename #首先探测该文件的编码
enconv filename #将其转换为你的locale编码,如Linux中普遍采用的UTF-8
enconv -x GB2312 filename

将小文件拼接为大文件
cat file1 file2 > file3 # 把file1与file2连接起来,输出到file3
cat -n file1 file2 > file3 # -n表示加行号

将大文件largefile切割成小文件
split -bytes=1m largefile outputprefix
split -bytes=40k largefile outputprefix
split -bytes=800b largefile outputprefix
注:outputprefix为指定输出文件的共同前缀,如mycorpus;该命令输出时默认加上两位排序字母为后缀,如aa, ab, ..., 最终输出的各文件名为mycorpusaa, mycorpusab, mycorpusac, ...

批量更改文件名
rename 's/\.bak$//' *.bak # 把文件名中的.bak全部去掉
rename 's/Ch(\d*)/第$1回/' *.txt # 把'Ch04'改为'第04回'

将pdf文件转换为纯文本文件
pdftotext filename.pdf filename.txt


字符串操作:

批量替换多个文本文件中的符合regexp的字符串为指定字符串string
sed -i 's/regexp/string/g' *.txt

去掉tag
sed -i 's/tag//g' *.txt
sed -i 's/_\w+//g' *.txt # 把 _NN0这种类型的tag去掉
sed -i 's/\/\w+//g' *.txt # 去掉Brown库所使用那种类型的tag
sed -i 's/\[[^]]+\]//g' *.txt # 去掉CLEC所使用那种类型的标注
sed -i 's/<[^>]*>//g' *.txt # 去掉<*>类型的标注


检索操作(许多内容待补充):

在文件中检索指定字符串(中文文本无需先分词)
grep Oliver OliverTwist.txt
grep 三个代表 20081211.txt
grep -w pain * # 在所有文件中寻找作为独立单词的pain,即不包括Spain/painful/pains/paint/painting/paints等,相当于AntConc软件中的Word选项
grep -i china * # 在所有文件中寻找china,不分大小写
grep vp3 *.txt > vp3.txt # 到CLEC/st目录里,在所有的5个库文件中寻找包含vp3的所有行,将结果输出到vp3.txt中
grep '<w VBG>' *.txt > vbg.txt # 找到所有包含<w VBG>的行,将结果输出到vbg.txt
注:关于在grep中使用正则表达式,见http://www.robelle.com/smugbook/regexpr.html

Windows下的一些优秀软件工具也有Linux版本,如
AntConc http://www.antlab.sci.waseda.ac.jp/software.html 该页面可下载各平台版本
ICTCLAS http://www.ictclas.org/Download.html 该页面有开源、共享等数个版本下载,都可以运行于Linux环境下

目前最方便易用的Linux发行版是Ubuntu(最新版本为9.10,代号为Karmic Koala)。Windows用户可以用Ubuntu带的一个小工具wubi.exe将Ubuntu安装到Windows系统中,就好像是一个普通的软件程序,非常方便。
http://www.ubuntu.com/ Ubuntu官方网站
http://forum.ubuntu.org.cn/ Ubuntu中文网上论坛
 
Last edited:
回复: Linux下的语料处理

补充:enca/enconv并非Ubuntu默认安装的,需要从源里单独安装,也很方便,用新立得(Synaptic)就是了
 
回复: Linux下的语料处理

补充:enca/enconv并非Ubuntu默认安装的,需要从源里单独安装,也很方便,用新立得(Synaptic)就是了

谢谢ArthurW,这样看来,在Linux下的语料处理就如同使用DOS命令一样.
只是不知道安装什么样的程序后,就可以进行此类操作了,楼主能不能提供一下?
 
回复: Linux下的语料处理

谢谢ArthurW,这样看来,在Linux下的语料处理就如同使用DOS命令一样.
只是不知道安装什么样的程序后,就可以进行此类操作了,楼主能不能提供一下?

只是安装一个Ubuntu系统就可以了,然后到命令行下执行操作。至于Linux的基本操作这里几句话也说不清,不过Ubuntu系统是非常易用的,推荐给愿意尝试的同仁,个人以为可以极大地增强productivity
 
Last edited:
回复: Linux下的语料处理

只是安装一个Ubuntu系统就可以了,然后到命令行下执行操作。至于Linux的基本操作这里几句话也说不清,不过Ubuntu系统是非常好用的,推荐给愿意尝试的同仁,个人以为可以极大地增强productivity

谢谢!
 
回复: Linux下的语料处理

顺便说一句,多数同仁可能是在Windows下工作,需要处理语料时临时转到Linux下即可,感觉好像把Linux系统当成一个非常强大的软件包
 
回复: Linux下的语料处理

嗯, Linux 下的power tools(sed, awk, grep等) 确实可以减少编程负担,在创建quick and dirty的程序原型和语料预处理上非常便捷。现在的Linux多有LiveCD版本(如Knoppix, Ubuntu),放到光驱从光盘启动就可以进入了,也不需要安装;还有从USB直接运行的,如CDLinux; 再有在Windows下用VMWare弄个虚拟机也可以把Linux当成一个程序来用。如有精力,还可以用Grub弄个Windows、Linux的双启动,使用上确实没有多大障碍,只是需要一点点时间和耐心来习惯它的操作。
 
回复: Linux下的语料处理

ArthurW和iCasino说的都很对。
国外很多开源的NLP软件都在Linux下运行,有时候要移植到窗口下很费事。
不过Perl虽然好用,可是太过灵活,古怪符号很多(个人感觉),python在这方面好点。
我最近思考的问题是,把perl或python的代码嵌入c++中,搞点混合编程。
经验不足,精力有限,高手请指教。
 
回复: Linux下的语料处理

ArthurW和iCasino说的都很对。
国外很多开源的NLP软件都在Linux下运行,有时候要移植到窗口下很费事。
不过Perl虽然好用,可是太过灵活,古怪符号很多(个人感觉),python在这方面好点。
我最近思考的问题是,把perl或python的代码嵌入c++中,搞点混合编程。
经验不足,精力有限,高手请指教。
是的,说到软件开发和编程,那更是Linux的长处所在;而对语料的预处理而言,其实还不需要懂得编程。至于perl,我觉得只要熟悉、用得好,无论哪种语言都不错,我就不觉得perl古怪:D
 
回复: Linux下的语料处理

Authur兄在这里给我们这些“窗口化”的C友们讲点Linux入门知识如何?
我superyangt一定搬条板凳认真听讲。还能提点问题。
搞个专题讲座吧!
 
回复: Linux下的语料处理

Authur兄在这里给我们这些“窗口化”的C友们讲点Linux入门知识如何?
我superyangt一定搬条板凳认真听讲。还能提点问题。
搞个专题讲座吧!
实不敢当,我远远没有那个水平,发这个帖只是给各位C友提供一些新的思路和工具而已
 
回复: Linux下的语料处理

我最近思考的问题是,把perl或python的代码嵌入c++中,搞点混合编程。

我对这个也很感兴趣。不过,我是反过来想,能否把其它语言写的程序,比如 c/c++/java 等 “镶嵌” 到 python 里面去,比如能否通过 python 调用 ICTCLAS 的中文分词,或调用 Stanford POS Tagger 进行英文的词性附码?还请各位 C 友指点。
 
回复: Linux下的语料处理

用C语言扩展Python的功能

肖文鹏 (xiaowp@263.net), 北京理工大学计算机系硕士研究生


2003 年 2 月 03 日

Pyton和C分别有着各自的优缺点,用Python开发程序速度快,可靠性高,并且有许多现成模块可供使用,但执行速度相对较慢;C语言则正好相反,其执行速度快,但开发效率低。为了充分利用两种语言各自的优点,比较好的做法是用Python开发整个软件框架,而用C语言实现其关键模块。本文介绍如何利用C语言来扩展Python的功能,并辅以具体的实例讲述如何编写Python的扩展模块。
一、简介

Python是一门功能强大的高级脚本语言,它的强大不仅表现在其自身的功能上,而且还表现在其良好的可扩展性上,正因如此,Python已经开始受到越来越多人的青睐,并且被屡屡成功地应用于各类大型软件系统的开发过程中。

与其它普通脚本语言有所不同,Python程序员可以借助Python语言提供的API,使用C或者C++来对Python进行功能性扩展,从而即可以利用Python方便灵活的语法和功能,又可以获得与C或者C++几乎相同的执行性能。执行速度慢是几乎所有脚本语言都具有的共性,也是倍受人们指责的一个重要因素,Python则通过与C语言的有机结合巧妙地解决了这一问题,从而使脚本语言的应用范围得到了很大扩展。

在用Python开发实际软件系统时,很多时候都需要使用C/C++来对Python进行扩展。最常见的情况是目前已经存在一个用C编写的库,需要在Python语言中使用该库的某些功能,此时就可以借助Python提供的扩展功能来实现。此外,由于Python从本质上讲还是一种脚本语言,某些功能用Python实现可能很难满足实际软件系统对执行效率的要求,此时也可以借助Python提供的扩展功能,将这些关键代码段用C或者C++实现,从而提供程序的执行性能。

本文主要介绍Python提供的C语言扩展接口,以及如何使用这些接口和C/C++语言来对Python进行功能性扩展,并辅以具体的实例讲述如何实现Python的功能扩展。


二、Python的C语言接口

Python是用C语言实现的一种脚本语言,本身具有优良的开放性和可扩展性,并提供了方便灵活的应用程序接口(API),从而使得C/C++程序员能够在各个级别上对Python解释器的功能进行扩展。在使用C/C++对Python进行功能扩展之前,必须首先掌握Python解释所提供的C语言接口。

2.1 Python对象(PyObject)

Python是一门面向对象的脚本语言,所有的对象在Python解释器中都被表示成PyObject,PyObject结构包含Python对象的所有成员指针,并且对Python对象的类型信息和引用计数进行维护。在进行Python的扩展编程时,一旦要在C或者C++中对Python对象进行处理,就意味着要维护一个PyObject结构。

在Python的C语言扩展接口中,大部分函数都有一个或者多个参数为PyObject指针类型,并且返回值也大都为PyObject指针。

2.2 引用计数

为了简化内存管理,Python通过引用计数机制实现了自动的垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。

下面的例子说明了Python解释器如何利用引用计数来对Pyhon对象进行管理:

例1:refcount.py
class refcount:
# etc.
r1 = refcount() # 引用计数为1
r2 = r1 # 引用计数为2
del(r1) # 引用计数为1
del(r2) # 引用计数为0,删除对象



在C/C++中处理Python对象时,对引用计数进行正确的维护是一个关键问题,处理不好将很容易产生内存泄漏。Python的C语言接口提供了一些宏来对引用计数进行维护,最常见的是用Py_INCREF()来增加使Python对象的引用计数增1,用Py_DECREF()来使Python对象的引用计数减1。

2.3 数据类型

Python定义了六种数据类型:整型、浮点型、字符串、元组、列表和字典,在使用C语言对Python进行功能扩展时,首先要了解如何在C和Python的数据类型间进行转化。

2.3.1 整型、浮点型和字符串

在Python的C语言扩展中要用到整型、浮点型和字符串这三种数据类型时相对比较简单,只需要知道如何生成和维护它们就可以了。下面的例子给出了如何在C语言中使用Python的这三种数据类型:

例2:typeifs.c
// build an integer
PyObject* pInt = Py_BuildValue("i", 2003);
assert(PyInt_Check(pInt));
int i = PyInt_AsLong(pInt);
Py_DECREF(pInt);
// build a float
PyObject* pFloat = Py_BuildValue("f", 3.14f);
assert(PyFloat_Check(pFloat));
float f = PyFloat_AsDouble(pFloat);
Py_DECREF(pFloat);
// build a string
PyObject* pString = Py_BuildValue("s", "Python");
assert(PyString_Check(pString);
int nLen = PyString_Size(pString);
char* s = PyString_AsString(pString);
Py_DECREF(pString);



2.3.2 元组

Python语言中的元组是一个长度固定的数组,当Python解释器调用C语言扩展中的方法时,所有非关键字(non-keyword)参数都以元组方式进行传递。下面的例子示范了如何在C语言中使用Python的元组类型:

例3:typetuple.c
// create the tuple
PyObject* pTuple = PyTuple_New(3);
assert(PyTuple_Check(pTuple));
assert(PyTuple_Size(pTuple) == 3);
// set the item
PyTuple_SetItem(pTuple, 0, Py_BuildValue("i", 2003));
PyTuple_SetItem(pTuple, 1, Py_BuildValue("f", 3.14f));
PyTuple_SetItem(pTuple, 2, Py_BuildValue("s", "Python"));
// parse tuple items
int i;
float f;
char *s;
if (!PyArg_ParseTuple(pTuple, "ifs", &i, &f, &s))
PyErr_SetString(PyExc_TypeError, "invalid parameter");
// cleanup
Py_DECREF(pTuple);



2.3.3 列表

Python语言中的列表是一个长度可变的数组,列表比元组更为灵活,使用列表可以对其存储的Python对象进行随机访问。下面的例子示范了如何在C语言中使用Python的列表类型:

例4:typelist.c
// create the list
PyObject* pList = PyList_New(3); // new reference
assert(PyList_Check(pList));
// set some initial values
for(int i = 0; i < 3; ++i)
PyList_SetItem(pList, i, Py_BuildValue("i", i));
// insert an item
PyList_Insert(pList, 2, Py_BuildValue("s", "inserted"));
// append an item
PyList_Append(pList, Py_BuildValue("s", "appended"));
// sort the list
PyList_Sort(pList);
// reverse the list
PyList_Reverse(pList);
// fetch and manipulate a list slice
PyObject* pSlice = PyList_GetSlice(pList, 2, 4); // new reference
for(int j = 0; j < PyList_Size(pSlice); ++j) {
PyObject *pValue = PyList_GetItem(pList, j);
assert(pValue);
}
Py_DECREF(pSlice);
// cleanup
Py_DECREF(pList);



2.3.4 字典

Python语言中的字典是一个根据关键字进行访问的数据类型。下面的例子示范了如何在C语言中使用Python的字典类型:

例5:typedic.c
// create the dictionary
PyObject* pDict = PyDict_New(); // new reference
assert(PyDict_Check(pDict));
// add a few named values
PyDict_SetItemString(pDict, "first",
Py_BuildValue("i", 2003));
PyDict_SetItemString(pDict, "second",
Py_BuildValue("f", 3.14f));
// enumerate all named values
PyObject* pKeys = PyDict_Keys(); // new reference
for(int i = 0; i < PyList_Size(pKeys); ++i) {
PyObject *pKey = PyList_GetItem(pKeys, i);
PyObject *pValue = PyDict_GetItem(pDict, pKey);
assert(pValue);
}
Py_DECREF(pKeys);
// remove a named value
PyDict_DelItemString(pDict, "second");
// cleanup
Py_DECREF(pDict);



三、Python的C语言扩展

3.1 模块封装

在了解了Python的C语言接口后,就可以利用Python解释器提供的这些接口来编写Python的C语言扩展,假设有如下一个C语言函数:

例6:example.c
int fact(int n)
{
if (n <= 1)
return 1;
else
return n * fact(n - 1);
}



该函数的功能是计算某个给定自然数的阶乘,如果想在Python解释器中调用该函数,则应该首先将其实现为Python中的一个模块,这需要编写相应的封装接口,如下所示:

例7: wrap.c
#include <Python.h>
PyObject* wrap_fact(PyObject* self, PyObject* args)
{
int n, result;

if (! PyArg_ParseTuple(args, "i:fact", &n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}



一个典型的Python扩展模块至少应该包含三个部分:导出函数、方法列表和初始化函数。

3.2 导出函数

要在Python解释器中使用C语言中的某个函数,首先要为其编写相应的导出函数,上述例子中的导出函数为wrap_fact。在Python的C语言扩展中,所有的导出函数都具有相同的函数原型:

PyObject* method(PyObject* self, PyObject* args);该函数是Python解释器和C函数进行交互的接口,带有两个参数:self和args。参数self只在C函数被实现为内联方法(built-in method)时才被用到,通常该参数的值为空(NULL)。参数args中包含了Python解释器要传递给C函数的所有参数,通常使用Python的C语言扩展接口提供的函数PyArg_ParseTuple()来获得这些参数值。

所有的导出函数都返回一个PyObject指针,如果对应的C函数没有真正的返回值(即返回值类型为void),则应返回一个全局的None对象(Py_None),并将其引用计数增1,如下所示:

PyObject* method(PyObject *self, PyObject *args)
{
Py_INCREF(Py_None);
return Py_None;
}



3.3 方法列表

方法列表中给出了所有可以被Python解释器使用的方法,上述例子对应的方法列表为:

static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};



方法列表中的每项由四个部分组成:方法名、导出函数、参数传递方式和方法描述。方法名是从Python解释器中调用该方法时所使用的名字。参数传递方式则规定了Python向C函数传递参数的具体形式,可选的两种方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递。

3.4 初始化函数

所有的Python扩展模块都必须要有一个初始化函数,以便Python解释器能够对模块进行正确的初始化。Python解释器规定所有的初始化函数的函数名都必须以init开头,并加上模块的名字。对于模块example来说,则相应的初始化函数为:

void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}



当Python解释器需要导入该模块时,将根据该模块的名称查找相应的初始化函数,一旦找到则调用该函数进行相应的初始化工作,初始化函数则通过调用Python的C语言扩展接口所提供的函数Py_InitModule(),来向Python解释器注册该模块中所有可以用到的方法。

3.5 编译链接

要在Python解释器中使用C语言编写的扩展模块,必须将其编译成动态链接库的形式。下面以RedHat Linux 8.0为例,介绍如何将C编写的Python扩展模块编译成动态链接库:

[xiaowp@gary code]$ gcc -fpic -c -I/usr/include/python2.2 \
-I /usr/lib/python2.2/config \
example.c wrapper.c
[xiaowp@gary code]$ gcc -shared -o example.so example.o wrapper.o



3.6 引入Python解释器

当生成Python扩展模块的动态链接库后,就可以在Python解释器中使用该扩展模块了,与Python自带的模块一样,扩展模块也是通过import命令引入后再使用的,如下所示:

[xiaowp@gary code]$ python
Python 2.2.1 (#1, Aug 30 2002, 12:15:30)
[GCC 3.2 20020822 (Red Hat Linux Rawhide 3.2-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.fact(4)
24
>>>



四、结束语

作为一门功能强大的脚本语言,Python将被更加广泛地应用于各个领域。为了克服脚本语言执行速度慢的问题,Python提供了相应的C语言扩展接口,通过将影响执行性能的关键代码用C语言实现,可以很大程度上提高用Python编写的脚本在运行时的速度,从而满足实际需要。




参考资料

可以从Python( http://www.python.org)网站着手了解所有关于Python的内容。
可以在Python网站上找到正式的Python C/API文档( http://www.python.org/doc/current/api/api.html)。
可以在Python网站上找到正式的编写Python扩展模块的文档( http://www.python.org/doc/current/api/api.html)。



关于作者



肖文鹏,是北京理工大学计算机系的一名硕士研究生,主要从事操作系统和分布式计算环境的研究,喜爱Linux和Python。你可以通过 xiaowp@263.net与他取得联系。


引自:http://www.ibm.com/developerworks/cn/linux/l-pythc/index.html
 
回复: Linux下的语料处理

I used to have Ubuntu installed, but honestly, I never used it. It just took up HDD space. I also had XP installed too, but I never really used that anymore either.
I tried KDE4 out a while back though, and I found the entire effects to be glitchy, and it's not like my video card couldn't handle it (it was a 8800GTS 512 mb). Compiz-Fusion worked great under Ubuntu, so I just came to the conclusion that the effects in KDE4 were poorly written.
So, have the fixed the effects? I remember the minimizing/maximizing effect being extremely buggy. I probably won't install Linux again until I get a new computer and make my current one a Linux box, but that won't be until around 2011, after I had graduated from college.
 
回复: Linux下的语料处理

GNU/Linux doesn't even need a GUI to run. KDE (4?) might be buggy, but you can try aother window manager, say, GNOME or XFCE, which typically consumes much less computer resources. GNU/Linux is all about choices. Don't get discouraged by some misbehaving applications running on it.
 
Back
顶部