目录
[3.2.关键词提取(TF-IDF 算法)](#3.2.关键词提取(TF-IDF 算法))
[4.与 Xapian 集成实战(中文检索核心流程)](#4.与 Xapian 集成实战(中文检索核心流程))
1.简介
Jieba 是中文分词领域的经典库,C++ 版本(jieba-cpp)兼顾 速度与精度,支持精确模式、全模式、搜索引擎模式,适配 CMake/vcpkg 工程化流程,是 Xapian 等全文检索库的最佳中文分词搭档。
Jieba 能精准切分中文文本,核心依赖两大算法,既保证常用词的精准度,又解决生僻词(未登录词)的识别问题:
1.核心分词算法:「前缀词典 + 双向最大匹配」
- 前缀词典 :将中文词典(如
jieba.dict.utf8)构建成前缀树(Trie 树),比如 "高性能" 的前缀包括 "高""高能""高性能",通过前缀树可快速匹配最长有效词; - 正向 / 反向最大匹配 :
- 正向最大匹配:从文本开头向后取最长可匹配的词(如 "中文分词"→"中文"+"分词");
- 反向最大匹配:从文本末尾向前取最长可匹配的词;
- Jieba 默认采用「正向最大匹配 + 反向最大匹配」结合的策略,选择分词结果更合理的方案(如减少单字词数量)。
2.未登录词识别:HMM 隐马尔可夫模型
对词典中没有的生僻词(如网络新词、专业术语),Jieba 基于 HMM 模型 + Viterbi 算法:
- 将中文分词转化为 "状态序列识别"(每个字的状态:B (词首)/M (词中)/E (词尾)/S (单字));
- 通过训练好的 HMM 模型(
hmm_model.utf8)预测未登录词的边界,比如 "ChatGPT""大模型" 这类新词也能准确切分。
3.关键词提取:TF-IDF 算法
核心逻辑:
- TF(词频):某词在文本中出现的频率,频率越高权重越高;
- IDF(逆文档频率):某词在所有文档中出现的频率,频率越低(越稀有)权重越高;
- 最终权重 = TF × IDF,以此筛选出文本中 "核心且稀有" 的关键词。
2.安装与集成
1.vcpkg 一键安装(推荐,自动处理依赖)
cpp
# Windows(x64)
vcpkg install jieba:x64-windows
# Linux/macOS(x64)
vcpkg install jieba:x64-linux
# 嵌入式 ARM 平台(如树莓派)
vcpkg install jieba:arm-linux
安装后自动配置词典路径(默认在 vcpkg/installed/<平台>/share/jieba/dict),CMake 可直接查找。
2.源码编译(适合定制化需求)
cpp
# 克隆源码
git clone https://github.com/yanyiwu/cppjieba.git
cd cppjieba && mkdir build && cd build
# 配置(指定安装路径)
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release
# 编译安装
make -j$(nproc) && sudo make install # Linux/macOS
3.CMake 工程集成(直接复用)
在 CMakeLists.txt 中添加配置,无需手动处理头文件和库路径:
cpp
cmake_minimum_required(VERSION 3.15)
project(JiebaDemo)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)
# 1. 查找 Jieba 库(vcpkg 自动配置,源码编译需确保安装路径在 CMAKE_PREFIX_PATH)
find_package(jieba REQUIRED)
if (jieba_FOUND)
message(STATUS "Jieba 找到:${jieba_INCLUDE_DIRS}")
endif()
# 2. 生成可执行文件,链接 Jieba
add_executable(jieba_demo main.cpp)
target_link_libraries(jieba_demo PRIVATE jieba::jieba)
3.核心功能代码示例
3.1.基础功能:三种分词模式
Jieba 支持三种核心分词模式,覆盖不同场景需求:
cpp
#include <jieba/Jieba.hpp>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 初始化 Jieba 分词器(全局单例,避免重复加载词典,提升性能)
static jieba::Jieba g_jieba(
// vcpkg 安装的词典路径(自动配置,无需修改)
jieba::GetDefaultDictPath(),
jieba::GetDefaultHmmPath(),
jieba::GetDefaultUserDictPath()
);
// 1. 精确模式:最常用,分词精准,无冗余(适合检索、数据分析)
void cut_exact(const string& text) {
vector<string> words;
g_jieba.Cut(text, words, true); // true = 精确模式
cout << "【精确模式】" << text << " → ";
for (size_t i = 0; i < words.size(); ++i) {
cout << (i > 0 ? "|" : "") << words[i];
}
cout << endl;
}
// 2. 全模式:遍历所有可能分词结果,无遗漏(适合关键词提取、模糊匹配)
void cut_full(const string& text) {
vector<string> words;
g_jieba.Cut(text, words, false); // false = 全模式
cout << "【全模式】" << text << " → ";
for (size_t i = 0; i < words.size(); ++i) {
cout << (i > 0 ? "|" : "") << words[i];
}
cout << endl;
}
// 3. 搜索引擎模式:在精确模式基础上,对长词二次拆分(适合全文检索)
void cut_search(const string& text) {
vector<string> words;
g_jieba.CutForSearch(text, words);
cout << "【搜索引擎模式】" << text << " → ";
for (size_t i = 0; i < words.size(); ++i) {
cout << (i > 0 ? "|" : "") << words[i];
}
cout << endl;
}
int main() {
string text = "C++ 开发者如何使用 Jieba 实现高性能中文分词";
cut_exact(text);
cut_full(text);
cut_search(text);
return 0;
}
3.2.关键词提取(TF-IDF 算法)
从文本中提取核心关键词,支持自定义权重和数量:
cpp
void extract_keywords(const string& text, size_t top_k = 5) {
vector<pair<string, double>> keywords;
// 提取前 top_k 个关键词(第二个参数为权重阈值,默认 0.0)
g_jieba.Extract(text, keywords, top_k, 0.0);
cout << "\n【关键词提取】" << text << " → " << endl;
for (const auto& kw : keywords) {
cout << kw.first << "(权重:" << kw.second << ")" << endl;
}
}
// 在 main 中调用
extract_keywords("C++ 高性能网络编程结合 Jieba 分词和 Xapian 检索,打造企业级中文搜索服务");
3.3.词性标注(需额外启用)
标注分词结果的词性(如名词、动词、形容词),需在 CMake 中启用词性标注功能:
cpp
# 修改 CMakeLists.txt,启用词性标注
find_package(jieba REQUIRED COMPONENTS pos_tag)
target_link_libraries(jieba_demo PRIVATE jieba::jieba jieba::pos_tag)
cpp
#include <jieba/PosTagger.hpp>
void pos_tag(const string& text) {
vector<pair<string, string>> tags;
jieba::PosTagger::Tag(g_jieba, text, tags);
cout << "\n【词性标注】" << text << " → " << endl;
for (const auto& tag : tags) {
// 词性说明:n=名词,v=动词,adj=形容词,x=标点
cout << tag.first << "(" << tag.second << ") ";
}
cout << endl;
}
// 在 main 中调用
pos_tag("嵌入式系统中 Jieba 分词的性能优化技巧");
4.与 Xapian 集成实战(中文检索核心流程)
结合之前的 Xapian 教程,实现 "中文分词 → 索引构建 → 中文检索" 完整链路:
cpp
#include <xapian.h>
// 中文分词 + Xapian 索引构建
void create_chinese_index(const string& db_path) {
try {
Xapian::WritableDatabase db(db_path, Xapian::DB_CREATE_OR_OPEN | Xapian::DB_COMPRESS);
vector<pair<string, string>> docs = {
{"文档1", "C++ 并发编程:互斥锁、条件变量与原子操作"},
{"文档2", "Boost.Asio 异步 IO 实现高性能网络通信"},
{"文档3", "Jieba 分词与 Xapian 结合打造中文全文检索系统"}
};
for (size_t i = 0; i < docs.size(); ++i) {
Xapian::Document xdoc;
string title = docs[i].first;
string content = docs[i].second;
string full_text = title + "\n" + content;
// 1. 中文分词(搜索引擎模式,提升检索召回率)
vector<string> title_words, content_words;
g_jieba.CutForSearch(title, title_words);
g_jieba.CutForSearch(content, content_words);
// 2. 添加关键词到 Xapian(标题权重 2,内容权重 1)
for (const auto& word : title_words) {
if (!word.empty()) xdoc.add_term("title:" + word, 2);
}
for (const auto& word : content_words) {
if (!word.empty()) xdoc.add_term("content:" + word, 1);
}
xdoc.set_data(full_text);
db.add_document(xdoc, i + 1); // 文档ID从1开始
}
db.commit();
cout << "\n中文索引创建成功!" << endl;
} catch (const Xapian::Error& e) {
cerr << "索引创建失败:" << e.get_msg() << endl;
}
}
// 中文检索(分词后查询)
void chinese_search(const string& db_path, const string& query_text) {
try {
Xapian::Database db(db_path, Xapian::DB_OPEN_READONLY | Xapian::DB_CACHE);
Xapian::Enquire enquire(db);
// 1. 对查询文本分词(搜索引擎模式)
vector<string> query_words;
g_jieba.CutForSearch(query_text, query_words);
// 2. 构建 Xapian 查询(多关键词 OR 逻辑)
Xapian::Query query;
for (const auto& word : query_words) {
if (word.empty()) continue;
query = Xapian::Query(Xapian::Query::OP_OR, query, Xapian::Query(word));
}
enquire.set_query(query);
Xapian::MSet results = enquire.get_mset(0, 10);
cout << "\n查询:" << query_text << " → 匹配 " << results.size() << " 条结果:" << endl;
for (const auto& item : results) {
cout << "文档ID:" << item.get_docid() << " 相关性:" << item.get_percent() << "%\n"
<< "内容:" << item.get_document().get_data() << "\n" << endl;
}
} catch (const Xapian::Error& e) {
cerr << "检索失败:" << e.get_msg() << endl;
}
}
// 在 main 中调用
int main() {
string db_path = "xapian_chinese_db";
create_chinese_index(db_path);
chinese_search(db_path, "中文检索 高性能");
return 0;
}
5.性能优化
1.初始化优化
- 单例模式 :分词器初始化时会加载词典(约几十 MB),务必全局单例(如示例中的
g_jieba),避免重复加载。 - 词典精简 :嵌入式场景可使用精简词典(
jieba.dict.small.utf8),减少内存占用(从~50MB 降至~10MB)。
cpp
#include <mutex>
#include <jieba/Jieba.hpp>
class JiebaSingleton {
public:
// 获取单例(懒汉式,线程安全)
static jieba::Jieba& GetInstance() {
static JiebaSingleton instance;
return instance.jieba_;
}
private:
jieba::Jieba jieba_;
static std::once_flag init_flag_;
// 私有构造函数,初始化分词器
JiebaSingleton() : jieba_(
jieba::GetDefaultDictPath(),
jieba::GetDefaultHmmPath(),
"user_dict.txt"
) {}
// 禁用拷贝/赋值
JiebaSingleton(const JiebaSingleton&) = delete;
JiebaSingleton& operator=(const JiebaSingleton&) = delete;
};
// 初始化静态变量
std::once_flag JiebaSingleton::init_flag_;
// 调用示例
auto& jieba = JiebaSingleton::GetInstance();
2.分词速度优化
- 批量分词 :对大量文本,批量调用
Cut而非单条调用,减少函数调用开销,速度提升 30%+。 - 禁用 HMM 模型 :无需处理未登录词(生僻词)时,可禁用 HMM 模型(
g_jieba.Cut(text, words, true, false)),速度提升 20%。
3.内存优化
- 词典内存映射 :源码编译时启用
USE_MMAP选项(cmake .. -DUSE_MMAP=ON),词典通过内存映射加载,减少物理内存占用。 - 及时释放资源 :分词后的
vector<string>无需保留时,及时清空(words.clear()),避免内存泄漏。
4.自定义词典
添加领域专属词汇(如技术术语),提升分词精度:
cpp
// 加载自定义词典(格式:词汇 词频 词性,词频和词性可选)
g_jieba.LoadUserDict("user_dict.txt");
user_dict.txt 内容示例:
cpp
Boost.Asio 100 n
Xapian 100 n
协程 50 v
cpp
// 继承Jieba类,重写Cut方法
class CustomJieba : public jieba::Jieba {
public:
using Jieba::Jieba;
void Cut(const string& text, vector<string>& words, bool is_cut_all, bool is_hmm = true) override {
// 1. 预处理:替换特殊字符(如将"C++"替换为"CPP",避免分词错误)
string processed_text = text;
replace(processed_text.begin(), processed_text.end(), '+', 'P');
// 2. 调用父类分词逻辑
Jieba::Cut(processed_text, words, is_cut_all, is_hmm);
// 3. 后处理:还原特殊字符
for (auto& w : words) {
replace(w.begin(), w.end(), 'P', '+');
}
}
};
5.多线程批量分词
对大量文本(如十万级文档),采用 "生产者 - 消费者" 模型批量分词:
cpp
#include <thread>
#include <queue>
#include <condition_variable>
// 线程安全的任务队列
class TaskQueue {
public:
void Push(const string& text) {
std::lock_guard<std::mutex> lock(mtx_);
queue_.push(text);
cv_.notify_one();
}
bool Pop(string& text) {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [this]() { return !queue_.empty() || stop_; });
if (stop_ && queue_.empty()) return false;
text = queue_.front();
queue_.pop();
return true;
}
void Stop() {
stop_ = true;
cv_.notify_all();
}
private:
std::queue<string> queue_;
std::mutex mtx_;
std::condition_variable cv_;
bool stop_ = false;
};
// 消费者线程函数
void Worker(TaskQueue& queue, vector<vector<string>>& results, std::mutex& res_mtx) {
auto& jieba = JiebaSingleton::GetInstance();
string text;
while (queue.Pop(text)) {
vector<string> words;
jieba.CutForSearch(text, words);
// 线程安全写入结果
std::lock_guard<std::mutex> lock(res_mtx);
results.push_back(words);
}
}
// 主函数调用
int main() {
TaskQueue queue;
vector<vector<string>> results;
std::mutex res_mtx;
// 启动4个消费者线程(根据CPU核心数调整)
vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back(Worker, std::ref(queue), std::ref(results), std::ref(res_mtx));
}
// 生产者:添加1000条文本任务
for (int i = 0; i < 1000; ++i) {
queue.Push("第" + to_string(i) + "条文本:Jieba与Xapian集成实现高性能中文检索");
}
// 停止队列,等待线程结束
queue.Stop();
for (auto& t : threads) {
t.join();
}
cout << "批量分词完成,处理文本数:" << results.size() << endl;
return 0;
}
6.总结
关键点回顾
- 核心原理:Jieba 基于「前缀词典 + 双向最大匹配」实现精准分词,结合 HMM 模型解决未登录词,TF-IDF 实现关键词提取;
- 核心价值:轻量级、高性能,支持三种分词模式,可通过自定义词典适配专业领域,是 C++ 项目中文分词的首选;
- 工程化要点:生产环境需封装为线程安全单例,嵌入式场景需精简词典 / 禁用 HMM,与 Xapian 集成时优先用「搜索引擎模式」提升检索召回率。
Jieba 的核心优势是「易用性 + 定制化」,短板是对超大规模文本的分布式处理支持不足;若需分布式分词,可结合 Kafka + 多节点 Jieba 实例实现,或搭配 Elasticsearch 使用。