segment
Version:
Chinese word segmentation 中文分词模块
354 lines (310 loc) • 8.99 kB
JavaScript
'use strict';
/**
* 分词器接口
*
* @author 老雷<leizongmin@gmail.com>
*/
var fs = require('fs');
var path = require('path');
var POSTAG = require('./POSTAG');
var Tokenizer = require('./Tokenizer');
var Optimizer = require('./Optimizer');
var debug = console.log;
/**
* 创建分词器接口
*/
var Segment = module.exports = function () {
this.POSTAG = POSTAG; // 词性
this.DICT = {}; // 词典表
this.modules = {
tokenizer: [], // 分词模块
optimizer: [] // 优化模块
};
this.tokenizer = new Tokenizer(this);
this.optimizer = new Optimizer(this);
};
/**
* 载入分词模块
*
* @param {String|Array|Object} module 模块名称(数组)或模块对象
* @return {Segment}
*/
Segment.prototype.use = function (module) {
var me = this;
if (Array.isArray(module)) {
module.forEach(function (module) {
me.use(module[i]);
});
} else {
if (typeof module == 'string') {
var filename = path.resolve(__dirname, 'module', module + '.js');
if (!fs.existsSync(filename)) {
throw Error('Cannot find module "' + module + '".');
} else {
module = require(filename);
}
}
// 初始化并注册模块
module.init(this);
this.modules[module.type].push(module);
}
return this;
};
Segment.prototype._resolveDictFilename = function (name) {
var filename = path.resolve(name);
if (!fs.existsSync(filename)) {
var filename = path.resolve(__dirname, '../dicts', name);
if (!fs.existsSync(filename)) {
throw Error('Cannot find dict file "' + filename + '".');
}
}
return filename;
};
/**
* 载入字典文件
*
* @param {String} name 字典文件名
* @param {String} type 类型
* @param {Boolean} convert_to_lower 是否全部转换为小写
* @return {Segment}
*/
Segment.prototype.loadDict = function (name, type, convert_to_lower) {
var filename = this._resolveDictFilename(name);
if (!type) type = 'TABLE'; // 默认为TABLE
// 初始化词典
if (!this.DICT[type]) this.DICT[type] = {};
if (!this.DICT[type + '2']) this.DICT[type + '2'] = {};
var TABLE = this.DICT[type]; // 词典表 '词' => {属性}
var TABLE2 = this.DICT[type + '2']; // 词典表 '长度' => '词' => 属性
// 导入数据
var POSTAG = this.POSTAG;
var data = fs.readFileSync(filename, 'utf8');
if (convert_to_lower) data = data.toLowerCase();
data.split(/\r?\n/).forEach(function (line) {
var blocks = line.split('|');
if (blocks.length > 2) {
var w = blocks[0].trim();
var p = Number(blocks[1]);
var f = Number(blocks[2]);
// 一定要检查单词是否为空,如果为空会导致Bug
if (w.length > 0) {
TABLE[w] = {f: f, p: p};
if (!TABLE2[w.length]) TABLE2[w.length] = {};
TABLE2[w.length][w] = TABLE[w];
}
}
});
return this;
};
/**
* 取词典表
*
* @param {String} type 类型
* @return {object}
*/
Segment.prototype.getDict = function (type) {
return this.DICT[type];
};
/**
* 载入同义词词典
*
* @param {String} name 字典文件名
*/
Segment.prototype.loadSynonymDict = function (name) {
var filename = this._resolveDictFilename(name);
var type = 'SYNONYM';
// 初始化词典
if (!this.DICT[type]) this.DICT[type] = {};
var TABLE = this.DICT[type]; // 词典表 '同义词' => '标准词'
// 导入数据
var data = fs.readFileSync(filename, 'utf8');
data.split(/\r?\n/).forEach(function (line) {
var blocks = line.split(',');
if (blocks.length > 1) {
var n1 = blocks[0].trim();
var n2 = blocks[1].trim();
TABLE[n1] = n2;
if (TABLE[n2] === n1) {
delete TABLE[n2];
}
}
});
return this;
};
/**
* 载入停止符词典
*
* @param {String} name 字典文件名
*/
Segment.prototype.loadStopwordDict = function (name) {
var filename = this._resolveDictFilename(name);
var type = 'STOPWORD';
// 初始化词典
if (!this.DICT[type]) this.DICT[type] = {};
var TABLE = this.DICT[type]; // 词典表 '同义词' => '标准词'
// 导入数据
var data = fs.readFileSync(filename, 'utf8');
data.split(/\r?\n/).forEach(function (line) {
line = line.trim();
if (line) {
TABLE[line] = true;
}
});
return this;
};
/**
* 使用默认的识别模块和字典文件
*
* @return {Segment}
*/
Segment.prototype.useDefault = function () {
this
// 识别模块
// 强制分割类单词识别
.use('URLTokenizer') // URL识别
.use('WildcardTokenizer') // 通配符,必须在标点符号识别之前
.use('PunctuationTokenizer') // 标点符号识别
.use('ForeignTokenizer') // 外文字符、数字识别,必须在标点符号识别之后
// 中文单词识别
.use('DictTokenizer') // 词典识别
.use('ChsNameTokenizer') // 人名识别,建议在词典识别之后
// 优化模块
.use('EmailOptimizer') // 邮箱地址识别
.use('ChsNameOptimizer') // 人名识别优化
.use('DictOptimizer') // 词典识别优化
.use('DatetimeOptimizer') // 日期时间识别优化
// 字典文件
.loadDict('dict.txt') // 盘古词典
.loadDict('dict2.txt') // 扩展词典(用于调整原盘古词典)
.loadDict('dict3.txt') // 扩展词典(用于调整原盘古词典)
.loadDict('names.txt') // 常见名词、人名
.loadDict('wildcard.txt', 'WILDCARD', true) // 通配符
.loadSynonymDict('synonym.txt') // 同义词
.loadStopwordDict('stopword.txt') // 停止符
;
return this;
};
/**
* 开始分词
*
* @param {String} text 文本
* @param {Object} options 选项
* - {Boolean} simple 是否仅返回单词内容
* - {Boolean} stripPunctuation 去除标点符号
* - {Boolean} convertSynonym 转换同义词
* - {Boolean} stripStopword 去除停止符
* @return {Array}
*/
Segment.prototype.doSegment = function (text, options) {
var me = this;
options = options || {};
var ret = [];
// 将文本按照换行符分割成多段,并逐一分词
text.replace(/\r/g, '\n').split(/(\n|\s)+/).forEach(function (section) {
var section = section.trim();
if (section.length < 1) return;
// ======================================
// 分词
var sret = me.tokenizer.split(section, me.modules.tokenizer);
// 优化
sret = me.optimizer.doOptimize(sret, me.modules.optimizer);
// ======================================
// 连接分词结果
if (sret.length > 0) ret = ret.concat(sret);
});
// 去除标点符号
if (options.stripPunctuation) {
ret = ret.filter(function (item) {
return item.p !== POSTAG.D_W;
});
}
// 转换同义词
function convertSynonym (list) {
var count = 0;
var TABLE = me.getDict('SYNONYM');
list = list.map(function (item) {
if (item.w in TABLE) {
count++;
return {w: TABLE[item.w], p: item.p};
} else {
return item;
}
});
return {count: count, list: list};
}
if (options.convertSynonym) {
do {
var result = convertSynonym(ret);
ret = result.list;
} while (result.count > 0);
}
// 去除停止符
if (options.stripStopword) {
var STOPWORD = me.getDict('STOPWORD');
ret = ret.filter(function (item) {
return !(item.w in STOPWORD);
});
}
// 仅返回单词内容
if (options.simple) {
ret = ret.map(function (item) {
return item.w;
});
}
return ret;
};
/**
* 将单词数组连接成字符串
*
* @param {Array} words 单词数组
* @return {String}
*/
Segment.prototype.toString= function (words) {
return words.map(function (item) {
return item.w;
}).join('');
};
/**
* 根据某个单词或词性来分割单词数组
*
* @param {Array} words 单词数组
* @param {Number|String} s 用于分割的单词或词性
* @return {Array}
*/
Segment.prototype.split = function (words, s) {
var ret = [];
var lasti = 0;
var i = 0;
var f = typeof s === 'string' ? 'w' : 'p';
while (i < words.length) {
if (words[i][f] == s) {
if (lasti < i) ret.push(words.slice(lasti, i));
ret.push(words.slice(i, i + 1));
i++;
lasti = i;
} else {
i++;
}
}
if (lasti < words.length - 1) {
ret.push(words.slice(lasti, words.length));
}
return ret;
};
/**
* 在单词数组中查找某一个单词或词性所在的位置
*
* @param {Array} words 单词数组
* @param {Number|String} s 要查找的单词或词性
* @param {Number} cur 开始位置
* @return {Number} 找不到,返回-1
*/
Segment.prototype.indexOf = function (words, s, cur) {
cur = isNaN(cur) ? 0 : cur;
var f = typeof s === 'string' ? 'w' : 'p';
while (cur < words.length) {
if (words[cur][f] == s) return cur;
cur++;
}
return -1;
};