原创声明:
由于工作需要,本人需要对日文内容进行处理。其中分词是一个无法绕过的环节。
经过一段时间的调研,发现kuromoji应该是目前应用范围最为广泛的分词器了。
在kuromoji的使用上,可以使用集成到Lucene之中的类库,也可以使用独立的版本。
独立版本的使用,可以参考: http://rensanning.iteye.com/blog/2008575 。此文说得已经非常详细了,举得例子也非常清晰。
有两点可以补充一下:
- 当前kuromoji的最新版本已经到了0.9.0, 可以到maven中央库看一下:http://search.maven.org/#search%7Cga%7C1%7Ckuromoji
- 文中提到的lucene-gosen 搜索了一下,最新的更新是2012年。 看起来项目已经废弃了
本文着重介绍的是使用Lucene集成的kuromoji分词器的使用方法。 在使用上比单独的使用要麻烦不少。希望此文对必须使用Lucene自带分词器的同学有所帮助。
1.分词器的创建
1 2 3 4 5 6 |
// 默认分词器 Analyzer kuromoji = new JapaneseAnalyzer(); // 自行构造的分词器 CharArraySet cas = new CharArraySet(DefaultConfig.JP_STOPWORDS, true); Analyzer kuromoji = new JapaneseAnalyzer(readDict(), Mode.SEARCH, cas, new HashSet<String>()); |
默认分词器并没有太多需要解释的地方。 下面主要解释一下自行构造的分词器
- 第一个参数: 读取用户词典。 后面会给一个参考方法。
- 第二个参数: Mode.Search 表示是Search模式。 一共有3种模式:
- Mode.Normal
- 官方解释:Default mode
- Mode.Search
- 官方解释:Uses a heuristic to segment compound nouns (複合名詞) into their parts
- Mode.Extend
- 官方解释:Same as SEARCH, but emits unigram tokens for unknown terms
- Mode.Normal
- 第三个参数: 停用词词表
- 日文的停用词还是很容易找到的。比如可以使用下面几个停用词的组合:
- 笔者惊奇的发现,连npmjs 之中都有日文停用词。 犀利
- 第四个参数:需要过滤掉的词性
- 官方例子可以到Lucene的unit test 之中查找。
- 笔者实验之后,发现效果并不是很好,可能默认去掉的词性太多,对预测模型不太友好,因此喂了一个空的Set进去。 各位读者可以根据自己需要进行构造
2. 使用分词器进行分词
这部分的代码非常乱,逻辑也很复杂,完全没法跟独立的版本的使用方法相比。
下面的代码仅供参考,如果您有更好的版本,还请贴一下代码让我也学习一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
private static String displayAllTokenInfo(String str, Analyzer a, Set<String> extraStopWords) { List<String> wordList = Lists.newLinkedList(); try { TokenStream stream = a.tokenStream("content", new StringReader(str)); PositionIncrementAttribute pis = stream.addAttribute(PositionIncrementAttribute.class); OffsetAttribute oa = stream.addAttribute(OffsetAttribute.class); CharTermAttribute cta = stream.addAttribute(CharTermAttribute.class); TypeAttribute ta = stream.addAttribute(TypeAttribute.class); stream.reset(); int lastOffset = -1; while (stream.incrementToken()) { if (oa.startOffset() < lastOffset) { continue; } lastOffset = oa.endOffset(); // System.out.print("["+cta+"]"); String curWord = cta.toString(); if (!extraStopWords.contains(curWord)) { wordList.add(curWord); } } // System.out.println(); stream.end(); } catch (IOException e) { e.printStackTrace(); } return StringUtils.join(wordList, " "); } |
这份代码笔者也不多解释了,笔者自己也没有完全的搞清楚。简单解释一下
“oa.startOffset() < lastOffset”
如果不进行这样的判断的话,就会出现重复的分词的情况。。。
由于数据保密需要,无法再次将所有的代码以及使用的数据公开出来,抱歉。
但是上面的代码是最核心的分词代码了。
3. 其他处理日文的代码
3.1 全角转半角
示例:
全角:1234ABCD
半角:1234ABCD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static String toDBC(String input) { char[] c = input.toCharArray(); for (int i = 0; i < c.length; i++) { if (c[i] == '\u3000') { c[i] = ' '; } else if ((c[i] > '\uFF00') && (c[i] < '\uFF5F')) { c[i] = (char) (c[i] - 65248); } } String returnString = new String(c); return returnString; } |
3.2 判断是否是日文字符
1 2 3 4 5 6 7 8 9 |
private static boolean isJapaneseByREG(String str) { if (str == null) { return false; } Pattern pattern = Pattern.compile("[\\u0800-\\u4e00]+"); return pattern.matcher(str.trim()).find(); } |
4. 总结
- 如果您有的选择,推荐选择不要与Lucene集成的版本,因为:
- 在使用上要复杂一些。
- 貌似无法调用Lemmatization、Reading功能
- 貌似无法切换自带词典。
- 哪位知道还请告知
- 就分词功能而言,Kuromoji可以自行构造分词器,包括增加用户词典、自行设置停用词与过滤掉部分词性的词。 同时设置好适应你自己的模型的分词模型,比如是默认的Normal还是Search or Extend
- Kuromoji的分词性能还没有测试,目测能满足大部分的性能要求。(笔者的环境上面2分钟进行了大概15W次分词。)

文章评论