本文既不是SWIG的介绍,也不是CRF++的说明,只是记录一下如何将CRF++通过SWIG封装出JNI接口以供Java调用。
其实Java通过Runtime直接调用CRF++的系统命令也是可以的,但那样比较麻烦,在我们项目中,分好的词是存储在Java对象(也就是内存)中的,如果以系统命令形式使用CRF++的话,需要将内容写到文本中以参数形式传递给CRF++的系统命令,然后再分析CRF++的输出,这样会涉及到IO操作,性能可能比较差。
关于如何以JNI方式使用CRF++的资料很少,但介绍SWIG将C/C++代码嵌入Java的资料很多,其中有些对我有指导意义:
http://hi.baidu.com/heaven_day/item/0e40aa31c479cbc4392ffa0c
http://www.nilday.com/%E5%AF%B9crf%E8%BF%9B%E8%A1%8Cjava%E5%B0%81%E8%A3%85/
在此对作者表示感谢!
CRF++的下载地址:https://code.google.com/p/crfpp/downloads/list
当前版本是0.58,下载列表中分Win32和Linux源码两套。如果只在LInux上使用,下载Linux那套就可以了;如果要在Windows上使用JNI方式,则两套都需要下载,因为在编译DLL动态链接库时需要用到LInux版中的文件。
下面说一下我编译DLL的操作步骤。
Linux系统使用CRF++相当简单,因为作者已经提供了Makefile文件,在CRF++安装好的情况下我们只需要两条make命令就可以编译出供JNI调用的libCRFPP.so链接库了,包括JNI的Java接口。
首先下载Linux版本发布包 CRF++-0.58.tar.gz ,然后将其解压并进入解压路径中,敲入以下命令安装:
% ./configure % make % su # make install
安装成功后进入解压路径中的swig目录,执行make命令;再进入解压路径中的java目录,再次执行make命令。这样java目录中就会生成JNI所需要的文件,其中有libCRFPP.so链接库和CRFPP.jar包。
如果想了解原理,可以打开两处的Makefile查看。
需要注意的是一般Linux发行版中已经安装了SWIG,如果没有话需要自己安装,关于Linux系统中的SWIG可以参看这里:http://codingstandards.iteye.com/blog/830342
Windows系统下编译DLL就困难多了,Windows本身不带有C/C++编译器,所以比较麻烦。本身我对编译命令也不熟悉,折腾了一天多也没弄成功,最后用了VC6.0开发环境编译成功,后来又去试了一下Visual Studio 2010,也编译成功,下面分别说一下编译步骤。
首先需要安装Windows版的SWIG,可以到这里下载:http://sourceforge.net/projects/swig/files/?source=navbar
Windows版的SWIG解压开就可以用了,不必安装,也可以将SWIG_HOME添加进系统环境变量,这样使用该命令时就不用每次都指定位置了。关于SWIG这里不作介绍,网上有很多相关的资料,可以先去了解一下。
我是通过虚拟机里的windowsXP编译的,因为VC6.0太老了,无法在Windows7和Windows8上正常运行。
首先下载Windows版本的发布包 CRF++-0.58.zip并解压,然后准备编译环境。安装虚拟机(我用的是VirtualBox)和WindowsXP(我用的是32位XP),然后在虚拟机中的 WinXP 操作系统中安装VC6.0,都准备好了之后,就开始编译了。
解压CRF++-0.58.zip后目录结构如下:
如果采用系统命令的话,用到的就是crf_learn.exe 、crf_test.exe和libcrfpp.dll三个文件,如果要编译成供JNI使用的DLL链接库这些文件是没有用的,要用的文件在sdk目录中。
sdk目录初始时只有三个文件,其中example.cpp是示例文件,没有用的:
SWIG是用来封装C++代码的,而使用SWIG需要提供接口文件(一般以 .i 作为扩展名)。这个接口文件我们可以从Linux发布包中获取,就在swig目录中,我们将其拷贝至此,如下:
打开CRFPP.i文件,修改倒数第二行代码,由%include ../crfpp.h改为%include crfpp.h,也就是说从当前路径包含crfpp.h文件,因为我们将该文件和CRFPP.i文件放在了同一目录下。
这时文件就准备好了,下面开始调用SWIG命令对C++进行封装,打开命令窗口,切换当前路径到CRFPP.i文件所在目录,然后执行如下命令:
需要注意的是要找到swig.exe命令,或者将SWIG解压路径加到path环境变量中,总之能定位到这个exe文件就行。另外要注意这里指定了Java包,这样生成的JNI接口文件中都会有包声明,最后需要将这些JNI接口打包到相应的jar中,不然调用会失败的。
执行完上述命令后该目录变化如下样子:
生成了几个Java文件以及CRFPP_wrap.cxx文件,Java文件最后打成jar时要用,接来处理CRFPP_wrap.cxx文件。
打开VC6.0,新建DLL工程(空的DLL工程):
然后在工程上面点右键,选择“添加文件到工程(F)”菜单项将CRFPP_wrap.cxx文件添加到该工程下:
在项目名上点右键并在弹出菜单中选择”设置“菜单,打开设置对话框。
在对话框选择”C/C++“选项卡,并在其分类下拉菜单中选择”预处理器“分类,在”附加标准包含路径“输入域中将JDK中的jni.h和jni_md.h两个文件所在路径填写上。一般情况下是%JAVA_HOME%\include和%JAVA_HOME%\include\win32两个路径,具体情况就要看你的JDK安装在什么地方了。我这里为了说明问题采取了%JAVA_HOME%变量形式,请将它换成具体的JDK安装路径。
再切换到”连接“选项卡,分类选择”输入“,在”附加库路径“中将libcrfpp.lib文件所在路径填写上,其实就是跟CRFPP.i同路径,因为我们一开始就将它们放在了一起。最后在”对象/库模块“输入域中填写上”libcrfpp.lib“并用空格与其它的库模块名分隔开。
到这里我们的准备工作就差不多做完了,其实我还可以添加其它配置,比如在构建完成后将JNI相关的Java文件编译成class文件,然后调用 jar命令打包,我这里没有做这些,jar包其实我是在Linux系统上编译CRF++链接库时生成的,直接拷来用了,同样我们也可以手动打包,但一定要注意包的结构,就是前面调用SWIG命令时指定的那个包,这里我用的是”org.chasen.crfpp“。至于怎么组织包结构以及打包,这里就不多说了。
最后调用”build“构建项目就可以了,项目上点右键也有快捷菜单,工具栏上也有快捷按钮,快捷键是F7,大功告成!!!查看一下项目路径下的Debug目录就会发现CRFPP.dll正静静地躺在那.......
另外:网上很多介绍文章都配置了 JAVA_BIN、JAVA_INCLUDE 或者 SWIG 等各种环境变量,我这里没有配置;也没有在这里完成.java文件的编译与打包,这个可以自行选择。
其实事情到里应该结束了才对,因为我们已经得到了可供JNI使用的CRFPP.dll动态链接库。但经理说现在的服务器用的是64位的Windows操作系统,要我看能否编译一份64位的DLL链接库,于是就有了下面这部分内容。
首先说明,64位的DLL没有编译成功,据个人估计是由于作者提供的libcrfpp.lib文件是32位的,根本无法编译成64位的DLL;另外Linux源码好像也不能放到Windows环境编译,总之这个尝试没有成功。但是在Visual Studio中编译32位的DLL是很容易的,不愿意折腾虚拟机的朋友可以用VS2010试一下。
Visual Studio我装在了64位的Win8系统上,前面准备是一样的,包括下载解压、拷贝CRFPP.i文件,还有命令行下调用SWIG生成JNI接口和C++包装文件CRFPP_wrap.cxx都是一样的,这里就不再赘述了。
下面仅描述一下编译CRFPP_wrap.cxx文件的过程。
首先将VS2010运行起来,新建一个Win32项目,如下图所示:
在随后的“Win32应用程序向导”点“下一步”进入下面的界面:
应用程序类型选择“DLL(D)",附加项勾选”空项目“(这好像不是必须的),最后点”完成“创建出一个空的DLL项目。
在项目名字上点右键,找到”添加“菜单项下的”现有项...“子菜单,点击后弹出添加现有项的对话框,将CRFPP_wrap.cxx文件添加到项目中。
然后在项目名字上点右键,点击”属性“菜单项,弹出”CRFPP属性页“对话框,我们将在这里对编译环境进行配置。
常规菜单项中可以更改”目标文件名“,默认是$(ProjectName)也就是项目名,我没有改变。
点击”VC++目录项“并进行如下配置:
然后再打开”链接器“选中”输入“子项,作如下配置:
至此配置完毕,在项目名上点右键,弹出菜单中选择”生成“,这样就可以成功导出CRFPP.dll动态链接库。
注:本人对C++以及Visual Studio一窍不通,全凭同事及网络博客指点,有什么说的不正确的地方,还请谅解!