You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何按句号分割字符串但忽略Mr.、Mrs.等头衔中的句号?

句子分割问题:忽略头衔中的句号

作为学校项目的一部分,我需要编写代码读取文件并将内容分割为句子。最初用strtok方法,把句号、问号、感叹号当成分隔符,基本能运行,但碰到Mr.、Mrs.、Ms.、Dr.这类头衔里的句号时,代码会误将其识别为句子分隔符。现在需要一种能按句号分割字符串,但自动忽略上述头衔中句号的解决方案。

现有代码

std::string buffer;

while(fileRead)
{
    std::getline(fileRead, buffer);
    char *temp = new char[buffer.length() + 1];
    strcpy(temp, buffer.c_str());
    char *q = std::strtok(temp, ".?!");

    while(q != NULL)
    {
        std::cout << q << std::endl;
        q = strtok(NULL, ".?!");
    }
}

问题示例

当输入文件内容为:

I am Mr. Smith.

现有代码输出:

I am Mr  
Smith

期望输出:

I am Mr. Smith

解决方案

strtok是基于简单字符匹配的分割工具,无法区分普通句号和头衔后的句号,所以换两种更灵活的实现思路:

方法一:手动遍历字符串(适合理解底层逻辑)

通过遍历字符串,逐个检查标点符号,判断当前句号是否属于头衔后缀,再决定是否分割:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cctype>

using namespace std;

// 判断当前位置的句号是否是头衔后的句号
bool isTitlePeriod(const string& s, size_t pos) {
    // 检查Mr.
    if (pos >= 2 && s.substr(pos-2, 2) == "Mr" && (pos == 2 || isspace(s[pos-3]))) {
        return true;
    }
    // 检查Mrs.
    if (pos >= 3 && s.substr(pos-3, 3) == "Mrs" && (pos == 3 || isspace(s[pos-4]))) {
        return true;
    }
    // 检查Ms.
    if (pos >= 2 && s.substr(pos-2, 2) == "Ms" && (pos == 2 || isspace(s[pos-3]))) {
        return true;
    }
    // 检查Dr.
    if (pos >= 2 && s.substr(pos-2, 2) == "Dr" && (pos == 2 || isspace(s[pos-3]))) {
        return true;
    }
    return false;
}

vector<string> splitSentences(const string& line) {
    vector<string> sentences;
    size_t start = 0;
    size_t lineLen = line.size();
    
    for (size_t i = 0; i < lineLen; ++i) {
        char c = line[i];
        // 遇到非头衔后缀的标点时分割句子
        if ((c == '.' || c == '?' || c == '!') && !isTitlePeriod(line, i)) {
            string sentence = line.substr(start, i - start);
            // 去除句子前后的空格
            size_t first = sentence.find_first_not_of(" \t");
            size_t last = sentence.find_last_not_of(" \t");
            if (first != string::npos && last != string::npos) {
                sentences.push_back(sentence.substr(first, last - first + 1));
            }
            start = i + 1;
        }
    }
    // 处理最后一段未分割的内容
    if (start < lineLen) {
        string sentence = line.substr(start);
        size_t first = sentence.find_first_not_of(" \t");
        size_t last = sentence.find_last_not_of(" \t");
        if (first != string::npos && last != string::npos) {
            sentences.push_back(sentence.substr(first, last - first + 1));
        }
    }
    return sentences;
}

int main() {
    ifstream fileRead("input.txt");
    string buffer;
    while (getline(fileRead, buffer)) {
        vector<string> sentences = splitSentences(buffer);
        for (const auto& sen : sentences) {
            cout << sen << endl;
        }
    }
    fileRead.close();
    return 0;
}

方法二:正则表达式(代码更简洁)

利用C++11及以后支持的正则表达式库,通过负向断言排除头衔后的句号,直接分割句子:

#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <vector>

using namespace std;

vector<string> splitSentences(const string& line) {
    vector<string> sentences;
    // 正则规则:匹配非头衔后的句号、问号、感叹号
    regex re(R"((?<!Mr|Mrs|Ms|Dr)\.|\?|!)");
    // 迭代器提取分隔符之间的内容
    sregex_token_iterator it(line.begin(), line.end(), re, -1);
    sregex_token_iterator end;
    
    for (; it != end; ++it) {
        string sen = *it;
        // 去除句子前后空格
        size_t first = sen.find_first_not_of(" \t");
        size_t last = sen.find_last_not_of(" \t");
        if (first != string::npos && last != string::npos) {
            sentences.push_back(sen.substr(first, last - first + 1));
        }
    }
    return sentences;
}

int main() {
    ifstream fileRead("input.txt");
    string buffer;
    while (getline(fileRead, buffer)) {
        vector<string> sentences = splitSentences(buffer);
        for (const auto& sen : sentences) {
            cout << sen << endl;
        }
    }
    fileRead.close();
    return 0;
}

两种方法对比

  • 手动遍历:逻辑直观,适合学习调试,后续要添加更多头衔(如Prof.)只需修改isTitlePeriod函数即可
  • 正则表达式:代码更紧凑,但需要熟悉正则语法,复杂场景下调试难度稍高

内容的提问来源于stack exchange,提问作者TRD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:43:15