Android切詞工具——BreakIterator(1)

本文介紹一下Android官方提供的切詞工具BreakIterator。

1.關於切詞

切詞是一個常見的需求,對於中文更爲重要,因爲類似英文一樣的以單詞爲中心的語言,以空格和標點符號爲天然分隔符。但中文不一樣,想要理解中文的準確含義,準確地切分詞是第一步。例如,搜索的時候,如果輸入是“看視頻”,用戶的意圖顯然是搜索視頻,如果按照整句匹配,顯然會欠召回。理想的情況應該是將“看視頻”切成“看”和“視頻”,並且識別出中心意圖詞是“視頻”,進而主要搜索視頻。當然,切詞的準確性就很重要了,是一切的基礎——如果切成“看視”和“頻”,或者“看”“視”“頻”,就適得其反了。

2.BreakIterator是個什麼玩意?

Android官方SDK已經給出了切詞工具BreakIterator,支持中文切詞。目前Android SDK中同時存在兩個BreakIterator:
java.text.BreakIterator
https://developer.android.com/reference/java/text/BreakIterator.html
android.icu.text.BreakIterator
https://developer.android.com/reference/android/icu/text/BreakIterator.html
前者API level 1,“根紅苗正”。後者API level 24,從Android 7.0引入。其實二者同源。
二者用法基本相同,以前者爲例,切一個句子,用法很簡單友好:

 public static void main(String args[]) {
      if (args.length == 1) {
          String stringToExamine = args[0];
          //print each word in order
          BreakIterator boundary = BreakIterator.getWordInstance();
          boundary.setText(stringToExamine);
          printEachForward(boundary, stringToExamine);
      }
 }

     public static void printEachForward(BreakIterator boundary, String source) {
         int start = boundary.first();
         for (int end = boundary.next();
              end != BreakIterator.DONE;
              start = end, end = boundary.next()) {
              System.out.println(source.substring(start,end));
         }
     }

(1)ICU和android.icu.text.BreakIterator
什麼是ICU?我們可以去官網看看:http://site.icu-project.org/ 。ICU - International Components for Unicode,這是一個處理國際化統一碼(Unicode)的開源項目,提供大量的自然語言文本處理功能,切詞只是其中之一。同時提供c/c++(ICU4C)和Java版本(ICU4J)。從官網上的這段話可以看出ICU的願景是提供跨平臺的統一編碼和國際化能力:
ICU is a mature, widely used set of C/C++ and Java libraries providing Unicode and Globalization support for software applications. ICU is widely portable and gives applications the same results on all platforms and between C/C++ and Java software.
ICU目前最新的版本是59.1(20170414)。
android.icu.text,是Android 7.0將開源項目ICU引入了Android,並做了一些改進。android.icu.text.BreakIterator是其中一個重要的API。
(2)java.text.BreakIterator
從源代碼看,java.text.BreakIterator是一個抽象類,由工廠方法getXXXInstance()提供實例:
Android 7.0以前:


    /**
     * Returns a new instance of {@code BreakIterator} to iterate over
     * word-breaks using the default locale.
     * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
     * @return a new instance of {@code BreakIterator} using the default locale.
     */
    public static BreakIterator getWordInstance() {
        return getWordInstance(Locale.getDefault());
    }

    /**
     * Returns a new instance of {@code BreakIterator} to iterate over
     * word-breaks using the given locale.
     */
    public static BreakIterator getWordInstance(Locale locale) {
        return new IcuIteratorWrapper(
                com.ibm.icu.text.BreakIterator.getWordInstance(locale));
    }

Android 7.0及以後:

    /**
     * Returns a new <code>BreakIterator</code> instance
     * for <a href="#word">word breaks</a>
     * for the {@linkplain Locale#getDefault() default locale}.
     *
     * @return A break iterator for word breaks
     */
    public static BreakIterator getWordInstance() {
        return getWordInstance(Locale.getDefault());
    }

    /**
     * Returns a new <code>BreakIterator</code> instance
     * for <a href="#word">word breaks</a>
     * for the given locale.
     *
     * @param locale the desired locale
     * @return A break iterator for word breaks
     * @throws NullPointerException if <code>locale</code> is null
     */
    public static BreakIterator getWordInstance(Locale locale) {
        return new IcuIteratorWrapper(
                android.icu.text.BreakIterator.getWordInstance(locale));
    }

哈哈,原來java.text.BreakIterator也是封裝了ICU。Android果然是開源大雜燴啊。
Android 7.0開始實際上直接用android.icu.text.BreakIterator了,兩者就是一回事了。
Android 7.0以前,是引入的包com.ibm.icu.text,從Android codebase來看,android.icu.text.BreakIterator位於
external/icu/icu4j/main/classes/core/src/com/ibm/icu/text/BreakIterator.java
看到實際上icu是作爲一個external庫,作爲Android源代碼的一部分而存在。external/icu包含icu4j和icu4c。

3.中文切詞效果測試

下面測試一下切詞效果。我目前的應用場景在Android 6以及下,所以測試java.text.BreakIterator。測試環境Android 6.0.1,測試代碼如下:

package com.example.testicu;

import android.text.TextUtils;

import java.text.BreakIterator;
import java.util.ArrayList;

public class Util {

    public static ArrayList<String> breakSentence(final String sentence) {
        final ArrayList<String> result = new ArrayList<String>();
        if (!TextUtils.isEmpty(sentence)) {
            final BreakIterator boundary = BreakIterator.getWordInstance();
            boundary.setText(sentence);
            try {
                int start = boundary.first();
                for (int end = boundary.next();
                     end != BreakIterator.DONE;
                     start = end, end = boundary.next()) {
                    String word = sentence.substring(start, end);
                    if (!TextUtils.isEmpty(word)) {
                        result.add(word);
                    }
                }
            } catch (IndexOutOfBoundsException e) {
                e.printStackTrace();
                result.clear();
            }
        }
        return result;
    }

}

(1)常規詞短語

原始詞 切詞結果
看視頻 看-視頻
聽音樂 聽-音樂
看小說 看-小說
遙控器 遙控-器
逛商場 逛-商場

可以看到常規詞效果尚可。

(2)比較新的專用名詞(以APP名爲例)和人名

原始詞 切詞結果
微信 微-信
微博 微-博
愛奇藝 愛-奇-藝
今日頭條 今日-頭條
勒布朗詹姆斯 勒-布朗-詹-姆-斯
邁克爾喬丹 邁-克-爾-喬-丹
張藝謀 張-藝-謀

新詞和人名基本無能爲力。

(3)切句子效果如何
隨便找一些中文句子來測試:

測試1:
楊振寧,1922年10月1日出生於安徽合肥,現任香港中文大學講座教授、清華大學教授、美國紐約州立大學石溪分校榮休教授[1] ,是中國科學院院士、美國國家科學院院士、臺灣“中央研究院”院士、俄羅斯科學院院士、英國皇家學會會員,1957年獲諾貝爾物理學獎;是中美關係鬆動後回中國探訪的第一位華裔科學家,積極推動中美文化交流和中美人民的互相瞭解;在促進中美兩國建交、中美人才交流和科技合作等方面,做出了重大貢獻。
結果1:
楊-振-寧-,-1922-年-10-月-1-日出生-於-安徽-合肥-,-現任-香港-中文-大學-講座-教授-、-清華大學-教授-、-美國-紐約-州立-大學-石-溪-分校-榮-休-教授-[-1-]- - -,-是-中國-科-學院-院士-、-美國-國家-科-學院-院士-、-臺灣-“-中央研究院-”-院士-、-俄羅斯-科-學院-院士-、-英國-皇家-學會-會員-,-1957-年-獲-諾貝爾-物理-學-獎-;-是-中美關係-鬆-動-後-回-中國-探訪-的-第-一位-華裔-科學-家-,-積極-推動-中美-文化-交流-和-中美-人民-的-互相-瞭解-;-在-促進-中美-兩國-建交-、-中美-人才-交流-和-科技-合作-等-方面-,-做出-了-重大-貢獻-。
測試2:
這首《飛鳥各投林》是《紅樓夢》十二釵曲裏的收尾曲,最早出現在第五回的時候,是以食盡鳥飛、唯餘白地的悲涼圖景,預示賈府未來子孫流散、十二釵花落斷腸的慘象。當時林黛玉還未進大觀園,這曲子就已爲四大家族的衰亡預先敲起了喪鐘。
結果2:
這-首-《-飛鳥-各-投-林-》-是-《-紅樓夢-》-十二-釵-曲-裏-的-收尾-曲-,-最早-出現-在-第五-回-的-時候-,-是以-食盡-鳥-飛-、-唯-餘白-地-的-悲-涼-圖-景-,-預示-賈-府-未來-子孫-流散-、-十二-釵-花落-斷腸-的-慘-象-。-當時-林黛玉-還-未進-大-觀-園-,-這-曲子-就-已-爲-四大-家族-的-衰亡-預先-敲起-了-喪鐘-。
測試3:
什麼是ICU?我們可以去官網看看:http://site.icu-project.org/ 。ICU - International Components for Unicode,這是一個處理國際化統一碼(Unicode)的開源項目,提供大量的自然語言文本處理功能,切詞只是其中之一。同時提供c/c++(ICU4C)和Java版本(ICU4J)。從官網上的這段話可以看出ICU的願景是提供跨平臺的統一編碼和國際化能力
結果3:
什麼-是-ICU-?-我們-可以-去-官-網-看看-:-http-:-/-/-site.icu—project.org-/- -。-ICU- — -International- -Components- -for- -Unicode-,-這-是-一個-處理-國際-化-統一-碼-(-Unicode-)-的-開-源-項目-,-提供-大量-的-自然-語言-文本-處理-功能-,-切-詞-只是-其中-之一-。-同時-提供-c-/-c-+-+-(-ICU4C-)-和-Java-版本-(-ICU4J-)-。-從-官-網上-的-這-段-話-可以-看出-ICU-的-願景-是-提供-跨-平臺-的-統一-編碼-和-國際-化-能力

可以看到對於長句子存在專用名詞較差、切詞過細的問題。

綜合來看,對於常規的、短句子尚可一用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章