C++实现NLP中文分词(附带源码)
一、项目背景详细介绍
中文自然语言处理(Natural Language Processing, NLP)是人工智能的重要分支之一。
其中最基础、也是最关键的环节之一就是——中文分词(Chinese Word Segmentation, CWS)。
与英文不同,中文文本中词语之间没有空格。
例如:
“我喜欢自然语言处理。”
在英文中,空格天然区分单词;而在中文中,计算机必须“猜测”出词语边界:
我 / 喜欢 / 自然语言 / 处理
这一步的准确与否,直接影响后续:
-
关键词提取;
-
文本分类;
-
情感分析;
-
机器翻译;
-
语音识别。
1. 中文分词方法发展历程
| 阶段 | 方法 | 特点 |
|---|---|---|
| 早期 | 基于规则的最大匹配(MM) | 简单高效、但精度受限 |
| 现代 | 基于统计(HMM、CRF) | 需大量语料、训练成本高 |
| 当代 | 深度学习模型(BiLSTM+CRF、BERT) | 精度高、泛化强、但实现复杂 |
本项目的目标不是实现复杂神经网络模型,而是构建一个:
C++ 实现的基于词典的中文分词系统
——结构清晰、可运行、适合教学。
2. 项目目的与目标
我们要实现一个轻量级的中文分词器:
✅ 支持正向最大匹配算法(FMM);
✅ 可加载外部词典;
✅ 支持多行输入文本;
✅ 输出切分结果;
✅ 跨平台(Windows/Linux/macOS);
✅ 纯 C++17 实现,无第三方依赖。
二、项目需求详细介绍
(1)功能性需求
-
读取词典文件(每行一个中文词语);
-
读取用户输入文本;
-
使用**正向最大匹配算法(FMM)**进行分词;
-
输出分词结果;
-
可支持不同最大词长(参数可配置);
-
允许输入多行文本;
-
输出格式清晰(词之间以“/”分隔)。
(2)性能与可靠性需求
-
能处理长达上万字的文本;
-
保证 O(n) 线性时间复杂度;
-
词典查找需高效(使用
unordered_set); -
错误输入有保护(空文本、空词典等);
-
程序运行稳定,不崩溃。
三、相关技术详细介绍
1. 中文分词问题本质
中文分词可视为:
给定字符串 S,将其切分为词语序列 [w1, w2, ..., wn],使得每个 wi 属于词典 D,且拼接后为原文本。
例如:
输入:我喜欢自然语言处理 输出:[我, 喜欢, 自然语言, 处理]
2. 最大匹配算法(Maximum Matching Algorithm)
(1)正向最大匹配(FMM)
从左到右扫描文本,每次取最大可能长度的词进行匹配。
若匹配失败,则截短一位继续匹配。
例子:
词典:{我, 喜欢, 自然, 自然语言, 语言, 处理}
文本:我喜欢自然语言处理
过程:
我 → 匹配成功; 喜欢 → 匹配成功; 自然语言 → 匹配成功; 处理 → 匹配成功。
结果:
我 / 喜欢 / 自然语言 / 处理
(2)反向最大匹配(RMM)
从右往左扫描。
(3)双向匹配(BiMM)
综合两者结果,选择更优划分。
3. 数据结构与实现技术
| 功能 | 技术 |
|---|---|
| 词典加载 | std::unordered_set<std::string> |
| 文件读取 | std::ifstream |
| 文本输入 | std::getline() |
| 中文字符 | UTF-8 编码,每个汉字占 3 字节 |
| 最大词长控制 | 整数参数 MAX_WORD_LEN |
| 分词输出 | std::vector<std::string> |
四、实现思路详细介绍
整体流程如下:
┌──────────────┐
│ 读取词典文件 │
└──────┬───────┘
↓
┌──────────────┐
│ 读取输入文本 │
└──────┬───────┘
↓
┌──────────────┐
│ 正向最大匹配 │
└──────┬───────┘
↓
┌──────────────┐
│ 输出分词结果 │
└──────────────┘
伪代码逻辑:
for i 从 0 到 len(text):
从当前位置取最长 max_word_len 个字符子串
若词典中存在该子串:
输出该词
i += 词长度
否则:
截短继续匹配
五、完整实现代码
/**********************************************************************************************
* 文件名 : chinese_segment.cpp
* 功能 : C++ 实现中文分词(正向最大匹配算法)
* 作者 : 教学示例
* 编译 : g++ -std=c++17 chinese_segment.cpp -o segment
* 运行 : ./segment
**********************************************************************************************/
#include <iostream>
#include <fstream>
#include <unordered_set>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
/**********************************************
* UTF-8 中文字符长度计算辅助函数
* (返回UTF-8一个汉字占用的字节数)
**********************************************/
int utf8_char_len(unsigned char c) {
if (c < 0x80) return 1; // ASCII
else if ((c >> 5) == 0x6) return 2;
else if ((c >> 4) == 0xE) return 3; // 中文
else if ((c >> 3) == 0x1E) return 4;
return 1;
}
/**********************************************
* 字符串切片:获取UTF-8子串
**********************************************/
string utf8_substr(const string& str, int start, int length) {
int i = 0, count = 0, bytePos = 0;
string result;
// 找起始位置
while (i < str.size() && count < start) {
i += utf8_char_len((unsigned char)str[i]);
count++;
}
// 提取 length 个字符
int begin = i;
while (i < str.size() && count < start + length) {
i += utf8_char_len((unsigned char)str[i]);
count++;
}
return str.substr(begin, i - begin);
}
/**********************************************
* 中文分词器类
**********************************************/
class ChineseSegmenter {
private:
unordered_set<string> dict; // 词典集合
int maxWordLen = 5; // 最大词长(可调)
public:
// 加载词典
bool loadDict(const string& filename) {
ifstream fin(filename);
if (!fin.is_open()) {
cerr << "词典文件打开失败: " << filename << endl;
return false;
}
string word;
while (getline(fin, word)) {
if (!word.empty()) dict.insert(word);
}
fin.close();
cout << "成功加载词典,共 " << dict.size() << " 个词" << endl;
return true;
}
// 设置最大词长
void setMaxWordLen(int len) {
maxWordLen = len;
}
// 正向最大匹配分词
vector<string> segment(const string& text) {
vector<string> result;
int pos = 0;
int textLen = 0;
// 统计总字符数(非字节)
for (int i = 0; i < text.size();)
textLen++, i += utf8_char_len((unsigned char)text[i]);
while (pos < textLen) {
int len = min(maxWordLen, textLen - pos);
string word;
bool matched = false;
// 从长到短匹配
for (; len > 0; len--) {
word = utf8_substr(text, pos, len);
if (dict.find(word) != dict.end()) {
matched = true;
break;
}
}
if (matched) {
result.push_back(word);
pos += len;
} else {
// 未匹配的单字
result.push_back(utf8_substr(text, pos, 1));
pos++;
}
}
return result;
}
};
/**********************************************
* 主函数
**********************************************/
int main() {
cout << "======================================" << endl;
cout << " C++ 中文分词系统 (FMM算法) " << endl;
cout << "======================================" << endl;
ChineseSegmenter segmenter;
segmenter.loadDict("dict.txt"); // 词典文件路径
segmenter.setMaxWordLen(5); // 设置最大词长
cout << "请输入中文文本(输入“exit”退出):" << endl;
string line;
while (true) {
cout << "\n>>> ";
getline(cin, line);
if (line == "exit") break;
if (line.empty()) continue;
vector<string> words = segmenter.segment(line);
cout << "分词结果: ";
for (size_t i = 0; i < words.size(); ++i) {
cout << words[i];
if (i != words.size() - 1) cout << "/";
}
cout << endl;
}
cout << "\n程序结束,再见!" << endl;
return 0;
}
六、代码详细解读
| 函数/类 | 功能说明 |
|---|---|
utf8_char_len() |
判断当前字节所代表的 UTF-8 字符长度 |
utf8_substr() |
截取 UTF-8 中文子串 |
ChineseSegmenter::loadDict() |
从文件加载中文词典 |
ChineseSegmenter::segment() |
使用正向最大匹配算法进行分词 |
main() |
用户交互输入文本并输出分词结果 |
七、项目详细总结
✅ 本项目实现了一个基于词典的中文分词系统;
✅ 使用 C++17 编写,支持 UTF-8 中文;
✅ 实现了正向最大匹配算法(FMM);
✅ 可加载自定义词典;
✅ 输出分词结果清晰直观;
✅ 适合作为 NLP 课程或博客教学范例。
八、项目常见问题及解答(FAQ)
Q1:词典文件格式是什么?
A1:每行一个词,如:
我 喜欢 自然语言 处理 北京 大学
Q2:为什么部分词无法识别?
A2:词典中未收录该词,可手动添加后重试。
Q3:UTF-8 中文截断乱码?
A3:本代码使用 utf8_char_len() 保证完整字符提取,不会出现乱码。
Q4:能否用来处理英文?
A4:可以,但对英文效果一般,可添加英文单词词典。
Q5:词典太大导致慢?
A5:可改用 unordered_map + Trie 树结构提升查找效率。
九、扩展方向与性能优化
-
加入反向最大匹配(RMM)算法
-
从右向左扫描;
-
与 FMM 结果比较取更优。
-
-
双向匹配(BIMM)算法
-
综合 FMM 和 RMM;
-
依据分词数、单字比例优化结果。
-
-
Trie 树词典结构
-
替换
unordered_set; -
加速前缀匹配。
-
-
概率分词(HMM模型)
-
引入状态转移矩阵;
-
基于维特比算法(Viterbi)求最优切分路径。
-
-
深度学习模型扩展
-
结合 CRF、BERT 模型;
-
提升语义感知能力。
-
-
多线程并行分词
-
将长文本切块分段分词;
-
提升处理速度。
-
-
词频统计与关键词提取
-
统计分词结果;
-
为后续 NLP 模块(如 TF-IDF)提供支持。
-
更多推荐
所有评论(0)