一、项目背景详细介绍

中文自然语言处理(Natural Language Processing, NLP)是人工智能的重要分支之一。
其中最基础、也是最关键的环节之一就是——中文分词(Chinese Word Segmentation, CWS)

与英文不同,中文文本中词语之间没有空格
例如:

“我喜欢自然语言处理。”

在英文中,空格天然区分单词;而在中文中,计算机必须“猜测”出词语边界:


我 / 喜欢 / 自然语言 / 处理

这一步的准确与否,直接影响后续:

  • 关键词提取;

  • 文本分类;

  • 情感分析;

  • 机器翻译;

  • 语音识别。


1. 中文分词方法发展历程

阶段 方法 特点
早期 基于规则的最大匹配(MM) 简单高效、但精度受限
现代 基于统计(HMM、CRF) 需大量语料、训练成本高
当代 深度学习模型(BiLSTM+CRF、BERT) 精度高、泛化强、但实现复杂

本项目的目标不是实现复杂神经网络模型,而是构建一个:

C++ 实现的基于词典的中文分词系统
——结构清晰、可运行、适合教学。


2. 项目目的与目标

我们要实现一个轻量级的中文分词器:

✅ 支持正向最大匹配算法(FMM)
✅ 可加载外部词典;
✅ 支持多行输入文本;
✅ 输出切分结果;
✅ 跨平台(Windows/Linux/macOS);
✅ 纯 C++17 实现,无第三方依赖。


二、项目需求详细介绍

(1)功能性需求

  1. 读取词典文件(每行一个中文词语);

  2. 读取用户输入文本

  3. 使用**正向最大匹配算法(FMM)**进行分词;

  4. 输出分词结果;

  5. 可支持不同最大词长(参数可配置);

  6. 允许输入多行文本;

  7. 输出格式清晰(词之间以“/”分隔)。


(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 树结构提升查找效率。


九、扩展方向与性能优化

  1. 加入反向最大匹配(RMM)算法

    • 从右向左扫描;

    • 与 FMM 结果比较取更优。

  2. 双向匹配(BIMM)算法

    • 综合 FMM 和 RMM;

    • 依据分词数、单字比例优化结果。

  3. Trie 树词典结构

    • 替换 unordered_set

    • 加速前缀匹配。

  4. 概率分词(HMM模型)

    • 引入状态转移矩阵;

    • 基于维特比算法(Viterbi)求最优切分路径。

  5. 深度学习模型扩展

    • 结合 CRF、BERT 模型;

    • 提升语义感知能力。

  6. 多线程并行分词

    • 将长文本切块分段分词;

    • 提升处理速度。

  7. 词频统计与关键词提取

    • 统计分词结果;

    • 为后续 NLP 模块(如 TF-IDF)提供支持。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐