:::

簡易PHP中文斷字器 / A Simple Chinese Word Tokenizer in PHP

image

這篇製作了一個簡單的斷字器,將「這份編號是tc_130的心靈錯位器真是太cool了」變成「這 份 編 號 是 tc _ 130 的 心 靈 錯 位 器 真 是 太 cool 了」。詳細來說,就是在不是英數字的文字前後加上空格,但是英數字則保持原樣。這樣子就能讓Apache Solr這樣的全文檢索引擎為內容索引時,就可以找到以中文一個字為單位的層級了。

 


PHP程式碼 / Code

function add_chinese_space( $content )
{
    if (is_array($content)) {
    $new_content = array();
    foreach($content AS $key => $value) {
      $new_content[$key] = add_chinese_space($value);
    }
    return $new_content;
  }
 
    $result = preg_replace_callback(
        "/([_]|[\W]|([\p{Han}]))/u",
        function ($matches) {
     
      if (preg_match_all("/[0-9\s]/", $matches[0])) {
        return $matches[0];
      }
      else {
        return " " . $matches[0] . " ";
      }
     
    },
        $content
    );
  $result = preg_replace('@[\x00-\x08\x0B\x0C\x0E-\x1F]@', ' ', $result);  // 避免Solr illegal characters
  $result = preg_replace("/\s+/", ' ', $result);
  $result = trim($result);
  return $result;
}

關於PHP的正規表達式,請看正規表示式 Regular Expression這篇的說明。

程式碼中有一個部分「$result = preg_replace('@[\x00-\x08\x0B\x0C\x0E-\x1F]@', ' ', $result);」是為了過濾Apache Solr不能接受的字元而做的處理,詳情請看「How can i omit the illegal characters,when indexing the docs?」這篇。

結果測試 / Test

輸入:

echo add_chinese_space("這份編號是tc_130的心靈錯位器真是太cool了");
echo add_chinese_space("這個布丁是在無聊的世界中找尋樂趣的一種不能吃的食物,喜愛動漫畫、遊戲、程式,以及跟世間脫節的生活步調。");
echo add_chinese_space("  測   試    看   看   ");
echo add_chinese_space("2013-03-24_23230021");

輸出

這 份 編 號 是 tc _ 130 的 心 靈 錯 位 器 真 是 太 cool 了
這 個 布 丁 是 在 無 聊 的 世 界 中 找 尋 樂 趣 的 一 種 不 能 吃 的 食 物 , 喜 愛 動 漫 畫 、 遊 戲 、 程 式 , 以 及 跟 世 間 脫 節 的 生 活 步 調 。
測 試 看 看
2013 - 03 - 24 _ 23230021

看起來還算ok。


小結 / Conclusion

許多做中文全文檢索的人都會說斷詞要用「詞」為單位,建議使用CKIP或是結巴這樣子的斷詞工具,他們可以將「下雨天留客天留我不留」斷成「下雨 天留客 天留 我不留」,詳細可以看看中文處理工具介紹這篇。

相對的,DSpace在使用Lucene在做索引時,就只是使用最簡單的一字詞斷字器ChineseAnalyzer。對「下雨天留客天留我不留」來說,就只「下 雨 天 留 客 天 留 我 不 留」。

兩種做法相比,如果使用者用單字「留」去查詢,前者會找不到結果,後者可以正確地找到結果。如果用詞「下雨」去查詢,前者找的到結果,後者會將查詢語句一樣做斷詞,變成「下」跟「雨」去搜尋,最後也能找到結果。

因此,將中文字以單字切開來,因為能夠查到所有「有出現這個字」的文件,就能夠滿足大部分資訊檢索中查全率的需求。而Apache Solr或Lucene這樣的全文檢索引擎會使用TF-IDF等方式來計算相關分數(score),再將結果依照相關分數遞減排序,所以也能達到部分查準率的需求。我在「全文搜尋引擎Lucene簡介投影片」這篇有講到Lucene的相關分數計算方法,可以查查看。

就結論來說,中文全文檢索引擎以中文單字斷詞來實作,用這篇的這個做法來斷詞,其實在很多場合就都派的上用場了。

總共2 則留言 ( 我要發問 , 隱藏留言 顯示留言 )

  1. 我做了一個JavaScript斷字器的版本,在這裡記錄一下:
    https://github.com/pulipulichen/jieba-js/blob/54350cae3ea95e18c326c6443a9237afcd979fcd/weka/spreadsheet2arff/script.js#L224

    var _add_chinese_space = function(_content) {
    if( Object.prototype.toString.call( _content ) === '[object Array]' ) {
    var _new_content = [];
    for (var _i = 0; _i < _content.length; _i++) {
    _new_content.push(_add_chinese_space(_content[_i]));
    }
    return _new_content;
    }

    var _result = _content;

    _result = _result.replace(/([_]|[\W])/g,function (_matches, _contents, _offset, _s) {
    if (_matches[0].match(/[0-9\s]/)) {
    return _matches[0];
    }
    else {
    return " " + _matches[0] + " ";
    }
    });
    _result = _result.replace(/@[\x00-\x08\x0B\x0C\x0E-\x1F]@/g, ' '); // 避免Solr illegal characters
    _result = _result.replace(/\s+/g, ' ');
    _result = _result.trim();
    return _result;
    };

    回覆刪除
  2. 對了,這也是實作「單字詞」、「一字詞」、「unigrams」的做法喔
    有需要的人可以參考看看

    絕對不是單純取字串的第i個字這麼簡單喔

    回覆刪除