hadoop上利用JNI分词
虽然有streaming方式,但是碍于本人蹩脚的C++,还是决定由JNI方式来进行分词,下面是具体环境:
hadoop:0.201
linux :2.6.16.60-0.21-TENCENT64-110923
jdk:Java(TM) SE Runtime Environment (build 1.6.0_17-b04),Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode)
TCWordSeg所必需的文件:
其中libTCWordSeg.so是分词的库文件,而TCWordSeg.jar中包含了所有的类和方法原型。
还有一个词典文件夹 data,其中包含了分词工具所必需的所有词典文件:
为了能更好更方便的使用分词,写了一个包装的类,在写代码之前,将TCWordSeg.jar添加到buildpath中。WordSeg4J 代码如下:
View Code
1 import java.io.IOException; 2 import org.mortbay.log.Log; 3 import com.tencent.research.nlp.*; 4 5 public class WordSeg4J { 6 7 public static String dictDir = "data"; 8 public static int SEG_MODE =1; 9 static{ 10 System.loadLibrary("TCWordSeg"); 11 SEG_MODE = TCWordSeg.TC_POS | TCWordSeg.TC_S2D 12 | TCWordSeg.TC_U2L | TCWordSeg.TC_T2S | TCWordSeg.TC_ENGU 13 | TCWordSeg.TC_CN | TCWordSeg.TC_USR; 14 15 try { 16 TCWordSeg.TCInitSeg(to_cstr_bytes(dictDir)); 17 } catch (IOException e) { 18 // TODO Auto-generated catch block 19 System.out.println(e.getMessage()); 20 Log.info(e.getMessage()); 21 } 22 } 23 24 public static String segString(String str) { 25 SWIGTYPE_p_void seghandle = TCWordSeg.TCCreateSegHandle(SEG_MODE); 26 //System.out.println(str); 27 String line_seg = ""; 28 /* 以 GBK 字节流的方式提供待切分句子 */ 29 try { 30 TCWordSeg.TCSegment(seghandle, to_cstr_bytes(str)); 31 int rescount = TCWordSeg.TCGetResultCnt(seghandle); 32 for (int i = 0; i < rescount; i++) { 33 byte[] word_bytes = TCWordSeg.TCGetWordAt(seghandle, i); 34 String word = new String(word_bytes, "GBK"); 35 line_seg += word + " "; 36 } 37 } catch (IOException e) { 38 // TODO Auto-generated catch block 39 System.out.println(e.getMessage()); 40 } 41 42 return line_seg; 43 } 44 45 /* 46 * 注意: 在 C 接口中传入参数类型为 char * 的地方, Java 接口中全部 使用参数类型 byte[], 并且 byte[] 数组必须以 47 * '\0' 结束。 这是为了解决GBK 编码字符串在 C <-> Java 中转换的问题 48 */ 49 public static byte[] to_cstr_bytes(String str) throws IOException { 50 String s = str + '\0'; 51 return s.getBytes("GBK"); 52 } 53 }
ps:如果您经常使用这个类,建议将其打包为jar,推荐使用eclipse的fatjar工具,将TCWordSeg.jar一起打包成一个jar,或者用ANT打包。
写完了包装类,就可以在Mapper或者Reducer中使用这个类的静态方法了。但是先别急着些驱动程序,还有两件事最好先完成:
1)分别将libTCWordSeg.so和data文件夹打包成lib.tar和data.tar,当然也可以打包成zip或者jar、gzippedtar等形式。
2)将上面两个tar文件上传到hdfs中,假设它们的hdfs路径分别为 /path/lib.tar 和/path/data.tar
好了,现在就可以写驱动程序了,在写驱动程序的时候,需要注意利用hadoop的一个重要特性:DistributedCache,关于这个东西的详细介绍,网络上一大堆,自己可以去看。
在驱动程序中,需要加入下面这么几行代码:
View Code
1 DistributedCache.createSymlink(conf); 2 3 Path filePath = new Path(" /path/lib.tar"); 4 String uriWithLink = filePath.toUri().toString(); DistributedCache.addCacheArchive(new URI(uriWithLink), conf); 5 6 Path filePath2 = new Path("/path/data.tar"); 7 String uriWithLink2 = filePath2.toUri().toString(); 8 DistributedCache.addCacheArchive(new URI(uriWithLink2), conf);
好了,之后用fatjar将程序打包,将TCWord.jar也一起打入,之后就可以在hadoop上运行程序了。