0

0

c++如何分割字符串_c++字符串分割实用方法集锦

穿越時空

穿越時空

发布时间:2025-09-22 16:21:01

|

353人浏览过

|

来源于php中文网

原创

C++中分割字符串需根据需求选择方法:简单分隔用find+substr性能高;注重简洁可用stringstream+getline;复杂模式选regex;避免使用strtok。

c++如何分割字符串_c++字符串分割实用方法集锦

C++里要分割字符串,说白了,就是把一个长字符串按照某个或某些分隔符,切成一小段一小段的子字符串,然后通常会把这些子字符串放到一个容器里,比如

std::vector
。这听起来简单,但实际操作起来,根据你的具体需求——比如是注重性能、代码简洁性,还是需要处理复杂的正则表达式,方法还真不少,各有各的适用场景和需要注意的地方。

解决方案

在C++中,分割字符串没有像Python那样内置的

split()
方法,我们通常需要自己动手,或者借助标准库中的一些工具。我个人最常用且推荐的,主要有以下几种:

1. 使用
std::string::find
std::string::substr
手动分割

这是最基础,也是最能体现C++“控制力”的一种方法。它的核心思想就是在一个循环里,不断地查找分隔符的位置,然后用

substr
截取分隔符之间的内容。

#include 
#include 
#include 

// 这是一个我自己经常会封装起来的工具函数
std::vector splitByFindSubstr(const std::string& s, char delimiter) {
    std::vector tokens;
    std::string::size_type start = 0;
    std::string::size_type end = s.find(delimiter);

    while (end != std::string::npos) {
        tokens.push_back(s.substr(start, end - start));
        start = end + 1; // 跳过分隔符
        end = s.find(delimiter, start);
    }
    // 添加最后一个token,因为循环会在最后一个分隔符处结束
    tokens.push_back(s.substr(start));
    return tokens;
}

// 示例用法:
// int main() {
//     std::string text = "apple,banana,orange,grape";
//     char delimiter = ',';
//     std::vector result = splitByFindSubstr(text, delimiter);
//     for (const auto& s : result) {
//         std::cout << s << std::endl;
//     }
//     // 输出:
//     // apple
//     // banana
//     // orange
//     // grape
//     return 0;
// }

个人看法: 这种方法虽然看起来有点“土”,需要写循环,但它的好处是性能通常不错,因为你对每次查找和截取都有明确的控制。对于简单的单字符分隔符,并且对性能有一定要求时,这往往是我的首选。缺点是,如果需要处理多个分隔符,或者分隔符是字符串,代码会稍微复杂一些。

立即学习C++免费学习笔记(深入)”;

2. 利用
std::stringstream
std::getline

这是C++标准库中一个非常优雅且常用的分割方式,尤其适合处理以行或特定分隔符分隔的数据流。

std::stringstream
可以把字符串当作输入流来操作,而
std::getline
可以从流中读取数据直到遇到指定分隔符。

#include 
#include 
#include 
#include  // 使用stringstream需要包含这个头文件

std::vector splitByStringStream(const std::string& s, char delimiter) {
    std::vector tokens;
    std::stringstream ss(s); // 将字符串s作为stringstream的初始内容
    std::string item;
    while (std::getline(ss, item, delimiter)) { // 从ss中读取,直到遇到delimiter
        tokens.push_back(item);
    }
    return tokens;
}

// 示例用法:
// int main() {
//     std::string text = "one;two;three;four";
//     char delimiter = ';';
//     std::vector result = splitByStringStream(text, delimiter);
//     for (const auto& s : result) {
//         std::cout << s << std::endl;
//     }
//     // 输出:
//     // one
//     // two
//     // three
//     // four
//     return 0;
// }

个人看法: 我觉得这种方法在代码简洁性和可读性上做得非常好。如果你处理的是CSV文件、日志文件,或者任何以固定分隔符组织的文本数据,

stringstream
getline
简直是神器。它的性能通常也足够好,但在极端性能敏感的场景下,可能会比纯粹的
find/substr
慢一点点,因为涉及到流操作的开销。但对于大多数应用来说,这点差异几乎可以忽略。

3. 使用 C 风格的
strtok
(慎用)

strtok
是C语言的函数,也能用来分割字符串。但它有很多陷阱,在现代C++编程中,我通常不推荐使用它,除非你真的清楚自己在做什么,并且有特定的历史代码兼容需求。

#include 
#include 
#include 
#include  // strtok需要这个头文件

// 示例用法:
// int main() {
//     char text_cstr[] = "alpha beta gamma delta"; // 注意:strtok会修改原字符串,所以需要可修改的char数组
//     const char* delimiter = " ";
//     
//     std::vector tokens;
//     char* token = strtok(text_cstr, delimiter);
//     while (token != nullptr) {
//         tokens.push_back(token);
//         token = strtok(nullptr, delimiter); // 后续调用传nullptr
//     }
//     for (const auto& s : tokens) {
//         std::cout << s << std::endl;
//     }
//     return 0;
// }

个人看法:

strtok
最大的问题是它不是线程安全的(它使用静态内部变量来保存状态),而且会修改原始字符串。这在多线程环境或需要保留原始字符串的场景下,简直是灾难。所以,如果不是万不得已,我建议你直接忽略这个方法,选择前面两种更安全、更现代的C++方法。

4. 使用正则表达式 (针对复杂模式)

如果你的分隔符不是简单的字符,而是一个复杂的模式(比如空白字符、多个不同的分隔符组合),那么

std::regex
就是你的救星。

#include 
#include 
#include 
#include  // 正则表达式需要这个头文件

std::vector splitByRegex(const std::string& s, const std::string& regex_str) {
    std::vector tokens;
    std::regex re(regex_str);
    // std::sregex_token_iterator 用于遍历匹配到的token
    // -1 表示我们想要的是不匹配正则表达式的部分(也就是分隔符之间的内容)
    std::sregex_token_iterator first{s.begin(), s.end(), re, -1}, last;
    for (; first != last; ++first) {
        if (!first->str().empty()) { // 避免添加空字符串,如果分隔符连续出现
            tokens.push_back(*first);
        }
    }
    return tokens;
}

// 示例用法:
// int main() {
//     std::string text = "  value1   value2,value3;value4  ";
//     // 分隔符可以是空格、逗号或分号,并处理连续分隔符和首尾空白
//     std::string regex_delimiter = "[ ,;]+"; // 匹配一个或多个空格、逗号或分号
//     std::vector result = splitByRegex(text, regex_delimiter);
//     for (const auto& s : result) {
//         std::cout << s << std::endl;
//     }
//     // 输出:
//     // value1
//     // value2
//     // value3
//     // value4
//     return 0;
// }

个人看法: 正则表达式的强大之处在于它能处理几乎任何复杂的分割需求。但它的代价是性能相对较低,而且代码的可读性也可能会因为正则表达式本身的复杂性而下降。所以,如果简单的字符分割能搞定,我不会轻易动用正则表达式。只有当分隔符规则非常复杂,或者需要灵活匹配多种分隔符时,它才成为不可替代的选择。


C++字符串分割,性能优化与常见陷阱有哪些?

谈到C++字符串分割,性能和陷阱是两个绕不开的话题。毕竟,C++的精髓之一就是对性能的追求,而字符串操作往往是性能瓶颈的常客。

性能优化:

Magician
Magician

Figma插件,AI生成图标、图片和UX文案

下载
  1. 选择合适的算法:
    • 对于简单的单字符分隔,
      std::string::find
      +
      std::string::substr
      的手动循环通常是最快的,因为它避免了流操作的额外开销。
    • std::stringstream
      +
      std::getline
      在大多数场景下性能足够好,并且代码简洁。如果字符串不是特别巨大,或者分割操作不是每秒百万次级别,它是个非常好的平衡点。
    • std::regex
      性能最低,因为它需要构建和匹配复杂的模式。只在必要时使用。
  2. 避免不必要的拷贝:
    std::string::substr
    会创建新的字符串对象,这意味着内存分配和数据拷贝。如果你的目标只是遍历这些“逻辑”上的子串,而不是真的需要拥有它们的拷贝,可以考虑传递
    std::string_view
    (C++17及以上)或者返回一个包含子串起始位置和长度的结构体,这样可以避免不必要的内存分配。不过,通常我们还是需要
    std::string
    的拷贝来独立使用这些片段。
  3. 预留
    vector
    容量:
    如果你知道大概会有多少个子串,可以提前用
    tokens.reserve(estimated_count)
    std::vector
    预留内存,这可以减少
    vector
    在添加元素时重新分配内存的次数,从而提升性能。
  4. 避免频繁的动态内存分配:
    std::string
    本身就是动态分配内存的,每次
    substr
    都会有潜在的分配。这在处理大量小字符串时影响不大,但在处理海量长字符串时,就值得关注了。

常见陷阱:

  1. 空字符串的处理:
    • 首尾分隔符: 比如
      " ,apple,banana,"
      用逗号分割,
      find/substr
      stringstream
      默认都会在开头和结尾产生空字符串。你可能需要手动过滤掉它们。
    • 连续分隔符: 比如
      "apple,,banana"
      ,中间的两个逗号会产生一个空字符串。这通常也是需要处理的。
      std::getline
      会把空字符串也提取出来,
      find/substr
      也会。
  2. strtok
    的副作用:
    前面提到了,
    strtok
    会修改原始字符串,且不是线程安全的。这是个大坑,能避则避。
  3. 分隔符是字符串而不是字符: 如果你的分隔符是
    "##"
    这样的字符串,
    std::string::find
    依然适用,但
    std::getline
    就无能为力了(它只接受
    char
    )。这时,
    find/substr
    std::regex
    是更好的选择。
  4. 编码问题: 如果处理的是非ASCII字符(比如中文),并且分隔符也是多字节字符,那么简单的
    char
    分隔符可能会出问题。你需要确保字符串和分隔符的编码一致,并可能需要使用专门的宽字符或多字节字符处理库。

如何处理多分隔符或空字符串情况下的C++字符串分割?

处理多分隔符和空字符串,是字符串分割中比较常见的“高级”需求。

处理多分隔符:

  1. 对于固定且不多的多分隔符(如逗号或分号):
    • 循环替换法: 可以先将所有不同的分隔符统一替换成一种,然后再进行单分隔符分割。比如,
      std::string text = "apple,banana;orange"
      ,你可以先将分号替换成逗号,再用逗号分割。
      // 替换函数示例
      void replaceAll(std::string& s, const std::string& from, const std::string& to) {
          size_t start_pos = 0;
          while((start_pos = s.find(from, start_pos)) != std::string::npos) {
              s.replace(start_pos, from.length(), to);
              start_pos += to.length(); // 确保从替换后的位置继续查找
          }
      }
      // 使用:
      // std::string data = "value1,value2;value3";
      // replaceAll(data, ";", ","); // 将分号替换为逗号
      // std::vector parts = splitByStringStream(data, ',');
    • 自定义
      find
      逻辑:
      find/substr
      的方法中,可以自定义
      find
      函数,让它查找多个分隔符中的任意一个。
      // 查找任意一个分隔符的位置
      std::string::size_type find_any_of(const std::string& s, const std::string& delimiters, std::string::size_type pos = 0) {
          return s.find_first_of(delimiters, pos);
      }
      // 然后在splitByFindSubstr中替换 s.find(delimiter) 为 find_any_of(s, " ,;", start)
  2. 对于复杂或不规则的多分隔符:
    • std::regex
      这是最强大也最推荐的方式。通过构建一个能匹配所有分隔符的正则表达式,可以非常灵活地处理各种情况。比如
      "[ ,;]+"
      可以匹配一个或多个空格、逗号或分号。上面的
      splitByRegex
      函数就是为此而生。

处理空字符串情况:

空字符串通常是由于以下几种情况产生的:

  • 字符串开头或结尾有分隔符:
    ,apple,banana,
  • 连续出现多个分隔符:
    apple,,banana

处理方法通常是在分割结果生成后进行过滤:

// 在 splitByFindSubstr 或 splitByStringStream 函数的末尾,或者调用后:
std::vector filtered_tokens;
for (const auto& token : tokens) {
    if (!token.empty()) { // 检查字符串是否为空
        filtered_tokens.push_back(token);
    }
}
// 也可以使用C++11的lambda和erase-remove idiom
// tokens.erase(std::remove_if(tokens.begin(), tokens.end(), [](const std::string& s){ return s.empty(); }), tokens.end());

std::regex
的例子中,我已经加入了
if (!first->str().empty())
的判断,就是为了避免将空字符串添加到结果中。

在我看来,处理这些“脏数据”是字符串分割的必经之路。你得明确你的业务逻辑是否需要保留空字符串,还是应该直接过滤掉。通常情况下,我们都是需要过滤掉的,除非空字符串本身具有某种业务含义。


C++字符串分割在实际项目中的应用场景与最佳实践

在实际项目中,字符串分割无处不在,从配置解析到数据处理,再到日志分析,它的身影随处可见。

应用场景:

  1. 配置文件解析: 比如
    key=value
    形式的配置项,用
    =
    分割。或者 CSV 文件,用
    ,
    分割。
  2. 日志分析: 日志行通常包含时间戳、级别、模块、消息等信息,这些信息往往用空格、
    |
    或其他特定字符分隔。
  3. 网络协议解析: 简单的文本协议中,消息头和消息体、参数之间可能用特定字符分隔。
  4. 命令行参数解析: 用户输入的命令和参数,通常以空格分隔。
  5. 数据清洗与转换: 从数据库或文件读取的原始字符串数据,需要分割成多个字段进行处理。

最佳实践:

  1. 明确需求,选择合适的方法:
    • 简单单字符分隔,注重性能:
      std::string::find
      +
      std::string::substr
    • 简单单字符分隔,注重代码简洁和流式处理:
      std::stringstream
      +
      std::getline
    • 复杂分隔符模式,或多分隔符:
      std::regex
    • 绝对避免
      strtok
  2. 封装成工具函数: 不要每次都写一遍分割逻辑。将常用的分割方法封装成可复用的函数,返回
    std::vector
    ,这样可以提高代码复用性和可读性。
  3. 考虑异常情况和边界条件:
    • 空输入字符串: 你的分割函数应该能优雅地处理空字符串输入,例如返回一个空的
      vector
    • 无分隔符的字符串: 比如
      text = "hello"
      ,用
      ,
      分割。结果应该包含一个
      "hello"
      。我的示例函数都能正确处理。
    • 连续分隔符和首尾分隔符: 根据业务需求,决定是保留空字符串还是过滤掉。
  4. 性能考量: 对于性能敏感的模块,使用性能分析工具(profiler)来确认字符串分割是否是瓶颈。如果是,再考虑更底层的优化,比如避免拷贝、使用
    string_view
    等。
  5. 编码一致性: 如果处理的是非ASCII字符,务必确保字符串和分隔符的编码一致,并使用相应的宽字符或多字节字符处理函数。
  6. 错误处理: 如果分割出的某些部分需要转换成数字或其他类型,要做好错误检查,比如
    std::stoi
    可能会抛出异常。

在我多年的开发经验里,字符串分割真的是个高频操作。很多时候,一个看似简单的分割,背后可能隐藏着各种边界条件和性能陷阱。所以,多思考一步,选择最适合当前场景的方法,并考虑好各种异常情况,才能写出健壮、高效的代码。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

401

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

620

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

354

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

259

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

606

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

531

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

646

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

604

2023.09.22

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 7.9万人学习

C 教程
C 教程

共75课时 | 4.3万人学习

C++教程
C++教程

共115课时 | 14.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号