From e9cbec02c2e52d9fea371eae61cd84c197f5a991 Mon Sep 17 00:00:00 2001 From: wyy Date: Sat, 29 Nov 2014 12:45:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=A4=E6=9D=A1=E8=AF=8D?= =?UTF-8?q?=E6=80=A7=E6=A0=87=E6=B3=A8=E7=9A=84=E8=A7=84=E5=88=99=EF=BC=8C?= =?UTF-8?q?=E9=92=88=E5=AF=B9=E8=BF=9E=E7=BB=AD=E8=8B=B1=E6=96=87=E5=92=8C?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChangeLog.md | 1 + README.md | 8 +++--- src/DictTrie.hpp | 2 +- src/PosTagger.hpp | 44 ++++++++++++++++++++++++++++++-- src/TransCode.hpp | 18 +++++++++++++ test/testdata/userdict.utf8 | 2 ++ test/unittest/TPosTagger.cpp | 49 +++++++++++++++++++++++------------- test/unittest/TTrie.cpp | 2 +- 8 files changed, 101 insertions(+), 25 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index dcbd172..01bab1b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,7 @@ 1. 修改两条更细粒度的特殊过滤规则,将连续的数字(包括浮点数)和连续的字母单独切分出来(而不会混在一起)。 2. 修改最大概率法时动态规划过程需要使用的 DAG 数据结构(同时也修改 Trie 的 DAG 查询函数),提高分词速度 8% 。 3. 使用了 `Aho-Corasick-Automation` 算法提速 Trie 查找的过程等优化,提升性能。 +4. 增加词性标注的两条特殊规则。 ## v2.4.3 diff --git a/README.md b/README.md index 822dd6f..7da17a4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ CppJieba是"结巴"中文分词的C++版本 ## 特性 + 源代码都写进头文件`src/*.hpp`里,`include`即可使用。 -+ 支持`utf-8, gbk`编码,但是推荐使用`utf-8`编码。 ++ 支持`utf-8, gbk`编码,但是推荐使用`utf-8`编码, 因为`gbk`编码缺少严格测试,慎用。 + 内置分词服务`server/server.cpp`,在linux环境下可安装使用。 + 项目自带较为完善的单元测试,核心功能中文分词(utf8)的稳定性接受过线上环境检验。 + 支持载自定义用户词典。 @@ -268,7 +268,7 @@ make && ./keyword.demo 详情请看 `test/tagging_demo.cpp`. ``` -["我:r", "是:v", "蓝翔:x", "技工:n", "拖拉机:n", "学院:n", "手扶拖拉机:n", "专业:n", "的:uj", "。:x", "不用:v", "多久:m", ",:x", "我:r", "就:d", "会:v", "升职:v", "加薪:nr", ",:x", "当:t", "上:f", "总经理:n", ",:x", "出任:v", "CEO:x", ",:x", "迎娶:v", "白富美:x", ",:x", "走上:v", "人生:n", "巅峰:n", "。:x"] +["我:r", "是:v", "蓝翔:x", "技工:n", "拖拉机:n", "学院:n", "手扶拖拉机:n", "专业:n", "的:uj", "。:x", "不用:v", "多久:m", ",:x", "我:r", "就:d", "会:v", "升职:v", "加薪:nr", ",:x", "当:t", "上:f", "总经理:n", ",:x", "出任:v", "CEO:eng", ",:x", "迎娶:v", "白富美:x", ",:x", "走上:v", "人生:n", "巅峰:n", "。:x"] ``` 支持自定义词性。 @@ -281,10 +281,10 @@ make && ./keyword.demo 结果如下: ``` -["我:r", "是:v", "蓝翔:nz", "技工:n", "拖拉机:n", "学院:n", "手扶拖拉机:n", "专业:n", "的:uj", "。:x", "不用:v", "多久:m", ",:x", "我:r", "就:d", "会:v", "升职:v", "加薪:nr", ",:x", "当:t", "上:f", "总经理:n", ",:x", "出任:v", "CEO:x", ",:x", "迎娶:v", "白富美:x", ",:x", "走上:v", "人生:n", "巅峰:n", "。:x"] +["我:r", "是:v", "蓝翔:nz", "技工:n", "拖拉机:n", "学院:n", "手扶拖拉机:n", "专业:n", "的:uj", "。:x", "不用:v", "多久:m", ",:x", "我:r", "就:d", "会:v", "升职:v", "加薪:nr", ",:x", "当:t", "上:f", "总经理:n", ",:x", "出任:v", "CEO:eng", ",:x", "迎娶:v", "白富美:x", ",:x", "走上:v", "人生:n", "巅峰:n", "。:x"] ``` -## 词典资料 +## 其它词典资料分享 + [dict.367W.utf8.tar.gz] iLife(`562193561@qq.com`) diff --git a/src/DictTrie.hpp b/src/DictTrie.hpp index 9d78dd6..10d86b5 100644 --- a/src/DictTrie.hpp +++ b/src/DictTrie.hpp @@ -21,7 +21,7 @@ namespace CppJieba const double MIN_DOUBLE = -3.14e+100; const double MAX_DOUBLE = 3.14e+100; const size_t DICT_COLUMN_NUM = 3; - const char* const UNKNOWN_TAG = "x"; + const char* const UNKNOWN_TAG = ""; class DictTrie { diff --git a/src/PosTagger.hpp b/src/PosTagger.hpp index 4939e02..a44956d 100644 --- a/src/PosTagger.hpp +++ b/src/PosTagger.hpp @@ -9,6 +9,10 @@ namespace CppJieba { using namespace Limonp; + static const char* const POS_M = "m"; + static const char* const POS_ENG = "eng"; + static const char* const POS_X = "x"; + class PosTagger { private: @@ -38,6 +42,7 @@ namespace CppJieba _dictTrie = _segment.getDictTrie(); LIMONP_CHECK(_dictTrie); }; + bool tag(const string& src, vector >& res) const { @@ -58,11 +63,46 @@ namespace CppJieba return false; } tmp = _dictTrie->find(unico.begin(), unico.end()); - res.push_back(make_pair(*itr, tmp == NULL ? "x" : tmp->tag)); + if(tmp == NULL || tmp->tag.empty()) + { + res.push_back(make_pair(*itr, _specialRule(unico))); + } + else + { + res.push_back(make_pair(*itr, tmp->tag)); + } } - tmp = NULL; return !res.empty(); } + private: + const char* _specialRule(const Unicode& unicode) const + { + size_t m = 0; + size_t eng = 0; + for(size_t i = 0; i < unicode.size() && eng < unicode.size() / 2; i++) + { + if(unicode[i] < 0x80) + { + eng ++; + if('0' <= unicode[i] && unicode[i] <= '9') + { + m++; + } + } + } + // ascii char is not found + if(eng == 0) + { + return POS_X; + } + // all the ascii is number char + if(m == eng) + { + return POS_M; + } + // the ascii chars contain english letter + return POS_ENG; + } }; } diff --git a/src/TransCode.hpp b/src/TransCode.hpp index a9a4d11..6b7c734 100644 --- a/src/TransCode.hpp +++ b/src/TransCode.hpp @@ -39,6 +39,24 @@ namespace CppJieba { return encode(uni.begin(), uni.end(), res); } + + // compiler is expected to optimized this function to avoid return value copy + inline string encode(Unicode::const_iterator begin, Unicode::const_iterator end) + { + string res; + res.reserve(end - begin); + encode(begin, end, res); + return res; + } + + // compiler is expected to optimized this function to avoid return value copy + inline Unicode decode(const string& str) + { + Unicode unicode; + unicode.reserve(str.size()); + decode(str, unicode); + return unicode; + } } } diff --git a/test/testdata/userdict.utf8 b/test/testdata/userdict.utf8 index b34db3b..0f76e49 100644 --- a/test/testdata/userdict.utf8 +++ b/test/testdata/userdict.utf8 @@ -2,3 +2,5 @@ 韩玉鉴赏 A B +iphone6 +蓝翔 nz diff --git a/test/unittest/TPosTagger.cpp b/test/unittest/TPosTagger.cpp index 051fd0c..81417c9 100644 --- a/test/unittest/TPosTagger.cpp +++ b/test/unittest/TPosTagger.cpp @@ -3,26 +3,41 @@ using namespace CppJieba; -const char * const QUERY_TEST1 = "我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。"; -const char * const ANS_TEST1 = "[\"我:r\", \"是:v\", \"蓝翔:x\", \"技工:n\", \"拖拉机:n\", \"学院:n\", \"手扶拖拉机:n\", \"专业:n\", \"的:uj\", \"。:x\", \"不用:v\", \"多久:m\", \",:x\", \"我:r\", \"就:d\", \"会:v\", \"升职:v\", \"加薪:nr\", \",:x\", \"当上:t\", \"总经理:n\", \",:x\", \"出任:v\", \"CEO:x\", \",:x\", \"迎娶:v\", \"白富:x\", \"美:ns\", \",:x\", \"走上:v\", \"人生:n\", \"巅峰:n\", \"。:x\"]"; -const char * const QUERY_TEST2 = "我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。"; -const char * const ANS_TEST2 = "[\"我:r\", \"是:v\", \"蓝翔:nz\", \"技工:n\", \"拖拉机:n\", \"学院:n\", \"手扶拖拉机:n\", \"专业:n\", \"的:uj\", \"。:x\", \"不用:v\", \"多久:m\", \",:x\", \"我:r\", \"就:d\", \"会:v\", \"升职:v\", \"加薪:nr\", \",:x\", \"当上:t\", \"总经理:n\", \",:x\", \"出任:v\", \"CEO:x\", \",:x\", \"迎娶:v\", \"白富:x\", \"美:ns\", \",:x\", \"走上:v\", \"人生:n\", \"巅峰:n\", \"。:x\"]"; +static const char * const QUERY_TEST1 = "我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。"; +static const char * const ANS_TEST1 = "[\"我:r\", \"是:v\", \"蓝翔:x\", \"技工:n\", \"拖拉机:n\", \"学院:n\", \"手扶拖拉机:n\", \"专业:n\", \"的:uj\", \"。:x\", \"不用:v\", \"多久:m\", \",:x\", \"我:r\", \"就:d\", \"会:v\", \"升职:v\", \"加薪:nr\", \",:x\", \"当上:t\", \"总经理:n\", \",:x\", \"出任:v\", \"CEO:eng\", \",:x\", \"迎娶:v\", \"白富:x\", \"美:ns\", \",:x\", \"走上:v\", \"人生:n\", \"巅峰:n\", \"。:x\"]"; +static const char * const QUERY_TEST2 = "我是蓝翔技工拖拉机学院手扶拖拉机专业的。不用多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。"; +static const char * const ANS_TEST2 = "[\"我:r\", \"是:v\", \"蓝翔:nz\", \"技工:n\", \"拖拉机:n\", \"学院:n\", \"手扶拖拉机:n\", \"专业:n\", \"的:uj\", \"。:x\", \"不用:v\", \"多久:m\", \",:x\", \"我:r\", \"就:d\", \"会:v\", \"升职:v\", \"加薪:nr\", \",:x\", \"当上:t\", \"总经理:n\", \",:x\", \"出任:v\", \"CEO:eng\", \",:x\", \"迎娶:v\", \"白富:x\", \"美:ns\", \",:x\", \"走上:v\", \"人生:n\", \"巅峰:n\", \"。:x\"]"; -TEST(PosTaggerTest, Test1) +static const char * const QUERY_TEST3 = "iphone6手机的最大特点是很容易弯曲。"; +static const char * const ANS_TEST3 = "[\"iphone6:eng\", \"手机:n\", \"的:uj\", \"最大:a\", \"特点:n\", \"是:v\", \"很:zg\", \"容易:a\", \"弯曲:v\", \"。:x\"]"; +//static const char * const ANS_TEST3 = ""; + +TEST(PosTaggerTest, Test) { PosTagger tagger("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8"); - vector > res; - tagger.tag(QUERY_TEST1, res); - string s; - s << res; - ASSERT_TRUE(s == ANS_TEST1); + { + vector > res; + tagger.tag(QUERY_TEST1, res); + string s; + s << res; + ASSERT_TRUE(s == ANS_TEST1); + } } -TEST(PosTaggerTest, Test2) +TEST(PosTagger, TestUserDict) { - PosTagger tagger("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../dict/user.dict.utf8"); - vector > res; - tagger.tag(QUERY_TEST2, res); - string s; - s << res; - ASSERT_TRUE(s == ANS_TEST2); + PosTagger tagger("../dict/jieba.dict.utf8", "../dict/hmm_model.utf8", "../test/testdata/userdict.utf8"); + { + vector > res; + tagger.tag(QUERY_TEST2, res); + string s; + s << res; + ASSERT_EQ(s, ANS_TEST2); + } + { + vector > res; + tagger.tag(QUERY_TEST3, res); + string s; + s << res; + ASSERT_EQ(s, ANS_TEST3); + } } diff --git a/test/unittest/TTrie.cpp b/test/unittest/TTrie.cpp index 5baea4a..49ce021 100644 --- a/test/unittest/TTrie.cpp +++ b/test/unittest/TTrie.cpp @@ -65,7 +65,7 @@ TEST(DictTrieTest, UserDict) ASSERT_TRUE(unit); string res ; res << *unit; - ASSERT_EQ("[\"20113\", \"35745\", \"31639\"] x -2.975", res); + ASSERT_EQ("[\"20113\", \"35745\", \"31639\"] -2.975", res); } TEST(DictTrieTest, automation)