mirror of
https://github.com/yanyiwu/cppjieba.git
synced 2025-07-18 00:00:12 +08:00
Merge https://github.com/aszxqw/cppjieba into dev
This commit is contained in:
commit
e6ce8e23f0
@ -13,7 +13,7 @@ ENDIF()
|
|||||||
ADD_SUBDIRECTORY(src)
|
ADD_SUBDIRECTORY(src)
|
||||||
ADD_SUBDIRECTORY(dict)
|
ADD_SUBDIRECTORY(dict)
|
||||||
|
|
||||||
if (!${APPLE})
|
IF("${CMAKE_SYSTEM}" MATCHES "Linux")
|
||||||
ADD_SUBDIRECTORY(script)
|
ADD_SUBDIRECTORY(script)
|
||||||
ADD_SUBDIRECTORY(conf)
|
ADD_SUBDIRECTORY(conf)
|
||||||
endif()
|
endif()
|
||||||
|
23
ChangeLog.md
Normal file
23
ChangeLog.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## v2.3.1
|
||||||
|
|
||||||
|
1. 修复安装时的服务启动问题(不过安装切词服务只是linux下的一个附加功能,不影响核心代码。)
|
||||||
|
|
||||||
|
|
||||||
|
## v2.3.0
|
||||||
|
|
||||||
|
1. 增加`KeywordExtractor.hpp`来进行关键词抽取。
|
||||||
|
2. 使用`gtest`来做单元测试。
|
||||||
|
|
||||||
|
## v2.2.0
|
||||||
|
|
||||||
|
1. 性能优化,提升切词速度约6倍。
|
||||||
|
2. 其他暂时也想不起来了。
|
||||||
|
|
||||||
|
## v2.1.1 (v2.1.1之前的统统一起写在 v2.1.1里面了)
|
||||||
|
|
||||||
|
1. 完成__最大概率分词算法__和__HMM分词算法__,并且将他们结合起来成效果最好的`MixSegment`。
|
||||||
|
2. 进行大量的代码重构,将主要的功能性代码都写成了hpp文件。
|
||||||
|
3. 使用`cmake`工具来管理项目。
|
||||||
|
4. 使用`Limonp`作为工具函数库,比如日志,字符串操作等常用函数。
|
||||||
|
5. 使用`Husky` 搭简易分词服务的服务器框架。
|
||||||
|
|
85
README.md
85
README.md
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
之所以全写成hpp文件,是因为这样在别的项目需要使用到中文分词功能的时候直接`#include"xx.hpp" `进来就可以使用,无需麻烦的链接。
|
之所以全写成hpp文件,是因为这样在别的项目需要使用到中文分词功能的时候直接`#include"xx.hpp" `进来就可以使用,无需麻烦的链接。
|
||||||
|
|
||||||
|
实践证明写成hpp使用起来真的很爽,在后面提到的在iOS应用中的使用,和包装成`Node.js`的扩展[NodeJieba]都特别顺利。
|
||||||
|
|
||||||
|
如果对代码细节感兴趣的请见 [代码详解]
|
||||||
|
|
||||||
## 中文编码
|
## 中文编码
|
||||||
|
|
||||||
现在支持utf8,gbk编码的分词。
|
现在支持utf8,gbk编码的分词。
|
||||||
@ -12,8 +16,8 @@
|
|||||||
|
|
||||||
### 依赖
|
### 依赖
|
||||||
|
|
||||||
* g++ (version >= 4.6);
|
* g++ (version >= 4.6 recommended);
|
||||||
* cmake (version >= 2.8);
|
* cmake (version >= 2.8 recommended);
|
||||||
|
|
||||||
### 下载和安装
|
### 下载和安装
|
||||||
|
|
||||||
@ -38,6 +42,8 @@ make test
|
|||||||
|
|
||||||
### 启动服务
|
### 启动服务
|
||||||
|
|
||||||
|
因为服务的后台运行需要`start-stop-daemon`,在ubuntu下是自带的。但是在CentOS下就需要自己安装了。
|
||||||
|
|
||||||
```
|
```
|
||||||
#Usage: /etc/init.d/cjserver {start|stop|restart|force-reload}
|
#Usage: /etc/init.d/cjserver {start|stop|restart|force-reload}
|
||||||
#启动
|
#启动
|
||||||
@ -147,58 +153,42 @@ Full方法切出所有字典里的词语。
|
|||||||
|
|
||||||
Query方法先使用Mix方法切词,对于切出来的较长的词再使用Full方法。
|
Query方法先使用Mix方法切词,对于切出来的较长的词再使用Full方法。
|
||||||
|
|
||||||
|
### 关键词抽取
|
||||||
|
|
||||||
## 模块详解
|
```
|
||||||
|
make && ./test/keyword.demo
|
||||||
|
```
|
||||||
|
|
||||||
本项目主要是如下目录组成:
|
you will see:
|
||||||
|
|
||||||
### src
|
```
|
||||||
|
我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。
|
||||||
|
->
|
||||||
|
["CEO:11.7392", "蓝翔:11.7392", "白富美:11.7392", "升职:10.8562", "加薪:10.6426"]
|
||||||
|
```
|
||||||
|
|
||||||
核心目录,包含主要源代码。
|
关键词抽取的demo代码请见`test/keyword_demo.cpp`
|
||||||
|
|
||||||
#### TrieManager模块
|
|
||||||
TrieManager.hpp 提供一个单例TrieManager,负责管理trie树。
|
|
||||||
通过该单例获取trie树时,会先判断是否已经由该字典文件生成了一颗trie树,如果已有则返回已有的trie树,否则重新创建一颗trie树返回。
|
|
||||||
|
|
||||||
#### Trie树
|
## 相关应用
|
||||||
Trie.hpp 负责载入词典的trie树,主要供Segment模块使用。
|
|
||||||
|
|
||||||
#### Segment模块
|
### 关于CppJieba的跨语言包装使用
|
||||||
|
|
||||||
MPSegment.hpp
|
|
||||||
(Maximum Probability)最大概率法:负责根据Trie树构建有向无环图和进行动态规划算法,是分词算法的核心。
|
|
||||||
|
|
||||||
HMMSegment.hpp
|
|
||||||
是根据HMM模型来进行分词,主要算法思路是根据(B,E,M,S)四个状态来代表每个字的隐藏状态。
|
|
||||||
HMM模型由dict/下面的`hmm_model.utf8`提供。
|
|
||||||
分词算法即viterbi算法。
|
|
||||||
|
|
||||||
FullSegment.hpp
|
|
||||||
枚举句子中所有可能成词的情况,找出字典里存在的即可。
|
|
||||||
|
|
||||||
#### TransCode模块
|
|
||||||
|
|
||||||
TransCode.hpp 负责转换编码类型,将utf8和gbk转换成`uint16_t`类型,也负责逆转换。
|
|
||||||
|
|
||||||
### src/Husky
|
|
||||||
|
|
||||||
提供服务的框架代码,
|
|
||||||
|
|
||||||
详见: https://github.com/aszxqw/husky
|
|
||||||
|
|
||||||
### src/Limonp
|
|
||||||
|
|
||||||
主要是一些工具函数,例如字符串操作等。
|
|
||||||
直接include就可以使用。
|
|
||||||
|
|
||||||
详见: https://github.com/aszxqw/limonp
|
|
||||||
|
|
||||||
## 关于CppJieba的跨语言包装使用
|
|
||||||
|
|
||||||
收到邮件询问跨语言包装(ios应用开发)使用的问题,这方面我没有相关的经验,建议参考如下python使用cppjieba的项目:
|
收到邮件询问跨语言包装(ios应用开发)使用的问题,这方面我没有相关的经验,建议参考如下python使用cppjieba的项目:
|
||||||
|
|
||||||
[jannson] 开发的供 python模块调用的项目 [cppjiebapy] , 和相关讨论 [cppjiebapy'discussion] .
|
[jannson] 开发的供 python模块调用的项目 [cppjiebapy] , 和相关讨论 [cppjiebapy_discussion] .
|
||||||
|
|
||||||
|
### NodeJieba
|
||||||
|
|
||||||
|
如果有需要在`node.js`中使用分词,不妨试一下[NodeJieba]。
|
||||||
|
|
||||||
|
### simhash
|
||||||
|
|
||||||
|
如果有需要在处理中文文档的的相似度计算,不妨试一下[simhash]。
|
||||||
|
|
||||||
|
## 演示
|
||||||
|
|
||||||
|
http://cppjieba-webdemo.herokuapp.com/
|
||||||
|
|
||||||
## 客服
|
## 客服
|
||||||
|
|
||||||
@ -206,12 +196,15 @@ TransCode.hpp 负责转换编码类型,将utf8和gbk转换成`uint16_t`类型
|
|||||||
|
|
||||||
## 鸣谢
|
## 鸣谢
|
||||||
|
|
||||||
"结巴中文"分词作者: SunJunyi
|
"结巴"中文分词作者: SunJunyi
|
||||||
https://github.com/fxsjy/jieba
|
https://github.com/fxsjy/jieba
|
||||||
|
|
||||||
顾名思义,之所以叫CppJieba,是参照SunJunyi大神的Jieba分词Python程序写成的,所以饮水思源,再次感谢SunJunyi。
|
顾名思义,之所以叫CppJieba,是参照Jieba分词Python程序写成的,所以饮水思源,再次感谢SunJunyi。
|
||||||
|
|
||||||
[CppJieba]:https://github.com/aszxqw/cppjieba
|
[CppJieba]:https://github.com/aszxqw/cppjieba
|
||||||
[jannson]:https://github.com/jannson
|
[jannson]:https://github.com/jannson
|
||||||
[cppjiebapy]:https://github.com/jannson/cppjiebapy
|
[cppjiebapy]:https://github.com/jannson/cppjiebapy
|
||||||
[cppjiebapy'discussion]:https://github.com/aszxqw/cppjieba/issues/1
|
[cppjiebapy_discussion]:https://github.com/aszxqw/cppjieba/issues/1
|
||||||
|
[NodeJieba]:https://github.com/aszxqw/nodejieba
|
||||||
|
[simhash]:https://github.com/aszxqw/simhash
|
||||||
|
[代码详解]:http://aszxqw.github.io/jekyll/update/2014/02/10/cppjieba-dai-ma-xiang-jie.html
|
||||||
|
@ -20,7 +20,7 @@ case "$1" in
|
|||||||
mkdir -p $RUNDIR
|
mkdir -p $RUNDIR
|
||||||
touch $PIDFILE
|
touch $PIDFILE
|
||||||
chmod 755 $RUNDIR
|
chmod 755 $RUNDIR
|
||||||
if start-stop-daemon --start --quiet --umask 007 --pidfile $PIDFILE --exec /bin/bash -- -c "$DAEMON $DAEMON_ARGS >> $LOGFILE 2>&1"
|
if start-stop-daemon --start --quiet --pidfile $PIDFILE --exec /bin/bash -- -c "$DAEMON $DAEMON_ARGS >> $LOGFILE 2>&1"
|
||||||
then
|
then
|
||||||
echo "$NAME."
|
echo "$NAME."
|
||||||
else
|
else
|
||||||
|
@ -138,8 +138,11 @@ namespace CppJieba
|
|||||||
itr ++;
|
itr ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
keywords.resize(MIN(topN, wordmap.size()));
|
keywords.clear();
|
||||||
partial_sort_copy(wordmap.begin(), wordmap.end(), keywords.begin(), keywords.end(), _cmp);
|
std::copy(wordmap.begin(), wordmap.end(), std::inserter(keywords, keywords.begin()));
|
||||||
|
topN = MIN(topN, keywords.size());
|
||||||
|
partial_sort(keywords.begin(), keywords.begin() + topN, keywords.end(), _cmp);
|
||||||
|
keywords.resize(topN);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
@ -153,7 +156,7 @@ namespace CppJieba
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool _cmp(const pair<string, uint>& lhs, const pair<string, uint>& rhs)
|
static bool _cmp(const pair<string, double>& lhs, const pair<string, double>& rhs)
|
||||||
{
|
{
|
||||||
return lhs.second > rhs.second;
|
return lhs.second > rhs.second;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test)
|
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test)
|
||||||
|
|
||||||
ADD_EXECUTABLE(segment.demo segment.cpp)
|
ADD_EXECUTABLE(segment.demo segment.cpp)
|
||||||
|
ADD_EXECUTABLE(keyword.demo keyword_demo.cpp)
|
||||||
ADD_EXECUTABLE(load_test load_test.cpp)
|
ADD_EXECUTABLE(load_test load_test.cpp)
|
||||||
ADD_SUBDIRECTORY(unittest)
|
ADD_SUBDIRECTORY(unittest)
|
||||||
|
13
test/keyword_demo.cpp
Normal file
13
test/keyword_demo.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "../src/KeywordExtractor.hpp"
|
||||||
|
using namespace CppJieba;
|
||||||
|
|
||||||
|
int main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
||||||
|
string s("我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。");
|
||||||
|
vector<pair<string, double> > wordweights;
|
||||||
|
size_t topN = 5;
|
||||||
|
extractor.extract(s, wordweights, topN);
|
||||||
|
cout<< s << "\n -> \n" << wordweights << endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
@ -3,63 +3,16 @@
|
|||||||
|
|
||||||
using namespace CppJieba;
|
using namespace CppJieba;
|
||||||
|
|
||||||
const char* KEYWORD_EXT_TEST_SENTENCE = "我来自北京邮电大学。 学号123456";
|
|
||||||
|
|
||||||
TEST(KeywordExtractorTest, Test1)
|
TEST(KeywordExtractorTest, Test1)
|
||||||
{
|
{
|
||||||
KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
||||||
const char* res[] = {"学号", "北京邮电大学"};
|
string s("我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。");
|
||||||
vector<string> words;
|
string res;
|
||||||
ASSERT_TRUE(extractor);
|
vector<pair<string, double> > wordweights;
|
||||||
ASSERT_TRUE(extractor.extract(KEYWORD_EXT_TEST_SENTENCE, words, 2));
|
size_t topN = 5;
|
||||||
ASSERT_EQ(words, vector<string>(res, res + sizeof(res)/sizeof(res[0])));
|
extractor.extract(s, wordweights, topN);
|
||||||
|
res << wordweights;
|
||||||
|
ASSERT_EQ(res, "[\"CEO:11.7392\", \"蓝翔:11.7392\", \"白富美:11.7392\", \"升职:10.8562\", \"加薪:10.6426\"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(KeywordExtractorTest, Test2)
|
|
||||||
{
|
|
||||||
KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
|
||||||
const char* res[] = {"学号", "北京邮电大学", "123456", "来自"};
|
|
||||||
vector<string> words;
|
|
||||||
ASSERT_TRUE(extractor);
|
|
||||||
ASSERT_TRUE(extractor.extract(KEYWORD_EXT_TEST_SENTENCE, words, 9));
|
|
||||||
ASSERT_EQ(words, vector<string>(res, res + sizeof(res)/sizeof(res[0])));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(KeywordExtractorTest, Test3)
|
|
||||||
{
|
|
||||||
ifstream ifs("../test/testdata/weicheng.utf8");
|
|
||||||
ASSERT_TRUE(!!ifs);
|
|
||||||
string str((istreambuf_iterator<char>(ifs)), (istreambuf_iterator<char>()));
|
|
||||||
KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
|
||||||
const char* res[] = {"柔嘉", "小姐", "孙小姐", "方鸿渐", "鸿渐"};
|
|
||||||
const char* res2 = "[\"柔嘉:5611.34\", \"小姐:4268.75\", \"孙小姐:3789.41\", \"方鸿渐:3030.35\", \"鸿渐:2552.93\"]";
|
|
||||||
vector<string> keywords;
|
|
||||||
string resStr;
|
|
||||||
vector<pair<string,double> > keywords2;
|
|
||||||
extractor.extract(str, keywords, 5);
|
|
||||||
extractor.extract(str, keywords2, 5);
|
|
||||||
ASSERT_EQ(keywords, vector<string>(res, res + sizeof(res)/sizeof(res[0])));
|
|
||||||
resStr << keywords2;
|
|
||||||
ASSERT_EQ(res2, resStr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//TEST(KeywordExtractorTest, Test4)
|
|
||||||
//{
|
|
||||||
// ifstream ifs("../test/testdata/weicheng.utf8");
|
|
||||||
// ASSERT_TRUE(!!ifs);
|
|
||||||
// string str((istreambuf_iterator<char>(ifs)), (istreambuf_iterator<char>()));
|
|
||||||
// KeywordExtractor extractor("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/idf.utf8");
|
|
||||||
// //const char* res[] = {"小姐", "孙小姐", "方鸿渐", "自己", "没有"};
|
|
||||||
// vector<pair<string,double> > keywords;
|
|
||||||
// extractor.extract(str, keywords, 5);
|
|
||||||
// //print(keywords);
|
|
||||||
// string res;
|
|
||||||
// res << keywords;
|
|
||||||
// print(keywords);
|
|
||||||
// print(__LINE__);
|
|
||||||
// exit(1);
|
|
||||||
// ASSERT_EQ(res, "[\"小姐:4268.75\", \"孙小姐:3789.41\", \"方鸿渐:3030.35\", \"自己:2300.54\", \"没有:2104.27\"]");
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user