簡易PHP中文斷字器 / A Simple Chinese Word Tokenizer in PHP
這篇製作了一個簡單的斷字器,將「這份編號是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的相關分數計算方法,可以查查看。
就結論來說,中文全文檢索引擎以中文單字斷詞來實作,用這篇的這個做法來斷詞,其實在很多場合就都派的上用場了。
我做了一個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;
};
對了,這也是實作「單字詞」、「一字詞」、「unigrams」的做法喔
回覆刪除有需要的人可以參考看看
絕對不是單純取字串的第i個字這麼簡單喔