如何使用cpp对中文字符串进行分隔

其实不只是中文,英文,emoji都可以分隔开来

代码如下:

// https://stackoverflow.com/questions/40054732/c-iterate-utf-8-string-with-mixed-length-of-characters
// C++ iterate utf-8 string with mixed length of characters
#include <iostream>
#include <string>
#include <vector>

std::vector <std::string> split_chinese(std::string s) {
    std::vector <std::string> t;
    for (size_t i = 0; i < s.length();) {
        int cplen = 1;
        // 以下的几个if,要参考这里 https://en.wikipedia.org/wiki/UTF-8#Description
        if ((s[i] & 0xf8) == 0xf0)      // 11111000, 11110000
            cplen = 4;
        else if ((s[i] & 0xf0) == 0xe0) // 11100000
            cplen = 3;
        else if ((s[i] & 0xe0) == 0xc0) // 11000000
            cplen = 2;
        if ((i + cplen) > s.length())
            cplen = 1;
        t.push_back(s.substr(i, cplen));
        i += cplen;
    }
    return t;
}

int main(int argc, char *argv[]) {
    std::string s = "我爱中文,我爱coding🤪";
    std::vector <std::string> t = split_chinese(s);
    for (auto a: t) {
        std::cout << a << std::endl;
    }
    return 0;
}

运行

build

clang++ SplitString.cpp -o splitString -std=c++17

run

./splitString

<> 一些背景知识: 1. 一个汉字在c\c++的存储, 使用2个字节(char)存储; 2. 汉字存储的第一个char, 其值一定大于'~'(0111 1110=126),否则将导致识别歧义; 此处, 使用"单ASCII字符"来表示非 单ASCII字符的判断, 可以根据以下规则(主要R3, R4); : 一个字符串的长度==1,那肯定是一个ASCII字符; : 一个字符串的长度==2,且第1个ASCII字符的编码小于'~'; 那肯定是2个单ASCII字符; -- (PS: "") : 一个字符串的长度==3,且中间的ASCII字符的编码小于'~'; 那第3个肯定是单ASCII字符; (前2个是否构成1个汉字不能确定) : (基于R3)如果第pos个位置的ASCII字符编码小于'~', 那从下标pos,pos+1处拆分字符串 (下标pos归前串),将不会导致乱码; A 或者 B 的选择,需要知道以下信息: (1). 从 cut_base_pos 开始到 pos_B 结束的这段字符串内, 最后一个单ASCII字符的结束下标 p 在此基础上, 若使用: y -- 表示单ASCII字符(已确定的); x -- 表示可能是 单ASCII字符,也可能是 汉字的半个ASCII存储码; 那么一段长度为N的字符串,按照存储的ASCII码可以表示为一串如下的字符串(不包含[]) "x .. x][x .. x y x .. x][x .. x", 其中 s,e表示当前正在分析的一段子串(0<=s<e<=N, 下标e-s = 期望的分割长度cut_size) ^ ^ ^ ^ ^ 0 s p e N 在 【s, e】 之间, 查找结束下标p 的思路: 从e开始向s查找, 找到 第一个y 后 break; 记录下标p, 则从位置p开始, 是一个正确的分割(cut); 但此分割并不是一个最好的分割. 可以在 位置p上, 再加上 2K 个长度, 使 p+2K 与 pos_B 最接近即可. 可以认为从2K个长度的内容是K个汉字(实际上并不一定..), 但并不影响这个最佳分割的正确性! :)
<think>我们在之前的对话中讨论过C++字符串分割函数,现在用户明确询问C++中用于切割字符串的函数。根据提供的引用,特别是引用[2]和引用[4],它们展示了两种不同的字符串分割实现方法。 引用[2]中的代码实现了一种常见的分割方法,它通过循环查找分隔符模式(pattern)来切割字符串。该函数接受两个参数:待分割字符串str和分隔符pattern,返回一个包含分割后子字符串的向量。 引用[4]中的代码则使用了另一种方法,它利用了find_first_not_of和find_first_of函数来定位分隔符之间的内容。这种方法可以处理多个连续的分隔符,并且代码中使用了标准库中的字符串操作函数。 此外,引用[1]和引用[3]提到了C++中的字符函数、字符串函数和内存函数,但没有直接给出分割字符串的函数。不过,它们强调了这些函数的重要性。 在C++标准库中,并没有一个直接名为“split”的函数,但我们可以使用<string>头文件中的成员函数来实现分割功能。常见的做法是使用find或find_first_of等函数来定位分隔符,然后用substr提取子串。 因此,我将向用户介绍两种常见的实现方法,并解释它们的原理。同时,我会提醒用户C++17引入了std::string_view,可以用于更高效的分割(避免拷贝),但考虑到兼容性,我将主要展示传统方法。 最后,按照要求生成相关问题。 </think>在C++中,标准库没有直接提供字符串切割函数,但可通过以下方法实现字符串分割: ### 方法1:使用`std::string::find`(引用[2]实现) ```cpp #include <vector> #include <string> std::vector<std::string> split(std::string str, std::string pattern) { std::vector<std::string> result; str += pattern; // 添加分隔符保证能处理结尾 size_t size = str.size(); for(size_t i = 0; i < size; i++) { size_t pos = str.find(pattern, i); if(pos < size) { result.push_back(str.substr(i, pos - i)); i = pos + pattern.size() - 1; // 跳过分隔符 } } return result; } ``` **特点**: - 显式添加分隔符确保处理边界 - 使用`find()`定位分隔符位置 - 时间复杂度:$O(n)$,$n$为字符串长度[^2] ### 方法2:使用`find_first_of`(引用[4]优化版) ```cpp void split(const std::string& s, std::vector<std::string>& tokens, const std::string& delim = " ") { tokens.clear(); size_t start = s.find_first_not_of(delim, 0); size_t end = s.find_first_of(delim, start); while(start != std::string::npos || end != std::string::npos) { tokens.push_back(s.substr(start, end - start)); start = s.find_first_not_of(delim, end); end = s.find_first_of(delim, start); } } ``` **优势**: - 自动跳过连续分隔符(如"a,,b") - 引用传参避免拷贝大字符串 - 支持自定义多字符分隔符[^4] ### 其他方案 1. **流处理法**(适合空格分隔): ```cpp std::vector<std::string> split_stream(const std::string& s) { std::istringstream iss(s); return {std::istream_iterator<std::string>{iss}, {}}; } ``` 2. **C++17 `std::string_view`**(零拷贝高效处理): ```cpp std::vector<std::string_view> split_sv(std::string_view strv, std::string_view delim = " ") { std::vector<std::string_view> output; for (auto pos = strv.find(delim); pos != strv.npos; ) { output.push_back(strv.substr(0, pos)); strv.remove_prefix(pos + delim.size()); } output.push_back(strv); return output; } ``` ### 性能对比 | 方法 | 时间复杂度 | 内存开销 | 适用场景 | |--------------------|------------|----------|------------------------| | `find`循环 | $O(n)$ | 中 | 通用分割 | | `find_first_of` | $O(n)$ | 低 | 含连续分隔符的情况 | | 流处理 | $O(n)$ | 高 | 简单空格分割 | | `string_view` | $O(n)$ | 最低 | C++17+ 高频切割大字符串[^4] | > 提示:对于包含中文等宽字符的字符串,需确保分隔符与字符串编码一致(如UTF-8使用多字节分隔符)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuanzhoulvpi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值