跳到主要内容
C++ string 类常用成员函数与全局函数详解 | 极客日志
C++
C++ string 类常用成员函数与全局函数详解 C++ std::string 类包含大量成员函数与全局函数用于文本处理。讲解 c_str 与 data 获取字符指针、copy 拷贝指定范围字符、find 系列查找匹配位置及 substr 截取子串。对比 find 与 rfind、find_first_of 等逻辑差异。介绍 operator+ 运算符重载拼接及 getline 读取整行输入,区分与 cin>>行为。涉及字符串转换、搜索、截取及输入输出核心用法。
MqEngine 发布于 2026/3/30 更新于 2026/6/5 23 浏览string 类中的一些成员函数
下面分别为大家带来详解,compare 由于用的不多,我们在这里不做解释,大家感兴趣的话可以自己去了解:
1. c_str
c_str() 会返回一个指向 std::string 内部字符数组的只读 const char* 指针 ,且这个字符数组以 \0(空字符)结尾 —— 完全符合 C 语言风格字符串的规范。
当你需要把 std::string 传给仅支持 C 风格字符串(char*/const char*)的函数时,必须用 c_str(),比如:
#include <iostream>
#include <string>
using namespace std;
int main () {
string filename ("Test.cpp" ) ;
FILE* fout = fopen (filename.c_str (), "r" );
if (fout) {
cout << "打开文件成功" << endl;
fclose (fout);
}
return 0 ;
}
C 语言文件操作函数 fopen:参数需要 const char*,必须用 c_str(),直接传 filename 会编译报错,C 语言打印函数 printf:%s 需要 C 风格字符串,必须用 c_str()...
总结
核心作用:将 C++ 的 std::string 转换为 C 风格的 const char* 字符串(以 \0 结尾),适配 C 语言函数;核心场景:调用 C 语言标准库函数(如 fopen、printf、strlen)时传参;核心禁忌:不修改返回的指针内容、不单独保存指针(需和原 std::string 对象同生命周期)。
2. data
data() 的核心功能和 c_str() 几乎一致:返回一个指向 std::string 内部字符数组的只读 const char* 指针 ,指向字符串的首字符。
但它的行为在 C++11 前后有个关键变化,这是理解它的核心:C++11 及以后 :data() 和 c_str() 完全等价 —— 返回的字符数组也以 \0 结尾,本质上是同一个函数的两种写法; : 只返回指向字符数组的指针, (仅包含有效字符),而 必须保证末尾有 。
C++11 之前
data()
不保证末尾有 \0
c_str()
\0
#include <iostream>
#include <string>
#include <cstdio>
int main () {
std::string str = "hello" ;
FILE* f1 = fopen (str.c_str (), "w" );
FILE* f2 = fopen (str.data (), "w" );
const char * ptr = str.data ();
for (int i = 0 ; i < str.size (); ++i) {
std::cout << ptr[i];
}
if (f1) fclose (f1);
if (f2) fclose (f2);
return 0 ;
}
总结 data() 核心作用:获取 std::string 内部字符数组的指针,C++11 后和 c_str() 等价(带 \0),C++11 前不带 \0;核心区别:仅存在于 C++11 前的 \0 结尾保证,C++11 后无差异;使用选择:适配 C 函数用 c_str()(语义更清),仅访问字符数据用 data(),C++11 后两者可互换。
3. get_allocator 简单来说:std::string 内部的字符数组(就是 c_str()/data() 指向的那块内存),并不是直接用 new 分配的,而是通过分配器(allocator) 来管理内存申请 / 释放。get_allocator() 就是返回这个分配器对象,让你能'复用'和 std::string 相同的内存分配规则。
分配器的本质是一个'内存管理工具',STL 容器默认用 std::allocator<char>,它的行为和普通的 new/delete 几乎一致,但提供了更灵活的内存管理接口。
4. copy copy() 的核心是:把 std::string 中指定范围的字符,拷贝到你提供的外部字符数组中 。和 strcpy/c_str() 不同的是:它不自动在目标数组末尾加 \0(需手动处理);可以只拷贝字符串的一部分(比如只拷贝前 5 个字符);直接操作字符数组,无需先转 C 风格字符串。
参数说明:dest:目标字符数组(你需要提前分配好内存);count:要拷贝的字符个数;pos:从原字符串的第 pos 个位置开始拷贝(默认从 0 开始);返回值:实际拷贝的字符数(通常等于 count,除非 pos+count 超出字符串长度)。
#include <iostream>
#include <string>
int main () {
std::string str = "Hello, C++ string!" ;
char buf[20 ] = {0 };
size_t copied = str.copy (buf, 5 );
buf[copied] = '\0' ;
std::cout << "示例 1:" << buf << std::endl;
str.copy (buf, 5 , 7 );
buf[5 ] = '\0' ;
std::cout << "示例 2:" << buf << std::endl;
size_t copied2 = str.copy (buf, 100 , 0 );
buf[copied2] = '\0' ;
std::cout << "示例 3:" << buf << "(实际拷贝" << copied2 << "个)" << std::endl;
return 0 ;
}
总结 核心作用:把 std::string 中指定范围的字符拷贝到外部字符数组,支持部分拷贝;核心特点:不自动加 \0、可指定拷贝起始位置和长度、越界自动截断;核心避坑:手动补 \0、保证目标数组内存足够;适用场景:精准提取字符串部分字符到字符数组,替代 strncpy 更灵活。
缺点:要手动分配数组、手动补 \0、要计算数组大小,稍不注意就会缓冲区溢出。
但是这个其实我们用的并不多,我们习惯于用 substr。
5. find(重点)
1. find find() 的核心是:从指定起始位置开始,在原字符串中查找第一个匹配的'字符 / 子串',返回其起始下标;如果没找到,返回 std::string::npos 。简单说,它就是字符串的'搜索定位器',支持查找单个字符、任意长度的子串。
#include <iostream>
#include <string>
using namespace std;
int main () {
string filename ("Testaxxx.cpp" ) ;
FILE* fout = fopen (filename.c_str (), "r" );
if (fout) {
cout << "打开文件成功" << endl;
fclose (fout);
}
size_t pos = filename.find ('.' );
if (pos != string::npos) {
string suffix = filename.substr (pos);
cout << suffix << endl;
}
return 0 ;
}
总结 核心作用:从指定位置开始查找字符 / 子串,返回第一个匹配的下标,没找到返回 npos;核心用法:find(目标,起始位置),支持字符、string、C 风格字符串三种查找目标;核心避坑:用 != npos 判断是否找到、注意大小写敏感、pos 越界直接返回 npos;核心场景:字符串匹配、分割、替换(比如找到子串位置后用 substr 截取)。
2. rfind rfind() 的核心是:从指定的'结束位置'开始,从右向左查找第一个匹配的字符 / 子串(即原字符串中最后一个出现的匹配项),找到返回其起始下标,没找到返回 std::string::npos 。简单说:find() 找'第一个',rfind() 找'最后一个'。
#include <iostream>
#include <string>
int main () {
std::string str = "Hello, C++! C++ is fun." ;
size_t pos1 = str.rfind ("C++" );
if (pos1 != std::string::npos) {
std::cout << "场景 1:最后一个\"C++\"在位置" << pos1 << std::endl;
}
size_t pos2 = str.rfind ("C++" , 10 );
if (pos2 != std::string::npos) {
std::cout << "场景 2:[0,10] 内最后一个\"C++\"在位置" << pos2 << std::endl;
}
size_t pos3 = str.rfind ('+' );
if (pos3 != std::string::npos) {
std::cout << "场景 3:最后一个'+'在位置" << pos3 << std::endl;
}
size_t pos4 = str.rfind ("Java" );
if (pos4 == std::string::npos) {
std::cout << "场景 4:未找到\"Java\"" << std::endl;
}
return 0 ;
}
总结 核心作用:从右往左查找字符 / 子串,返回最后一个匹配的起始下标,没找到返回 npos;核心差异:和 find() 比,查找方向相反、pos 参数是'结束范围'而非'起始位置';核心避坑:正确理解 pos 的含义、用 != npos 判断结果;核心场景:找最后一个分隔符(小数点、斜杠、逗号等)、提取后缀 / 文件名。
简单说,rfind() 是'反向查找神器',尤其适合处理文件路径、后缀名这类需要找'最后一个匹配项'的场景,搭配 substr() 能快速完成字符串拆分。
3. find_first_of find_first_of() 的核心是:从指定起始位置开始,在原字符串中找第一个属于'目标字符集合'的字符,返回其下标;没找到则返回 npos 。
简单对比理解:find("abc"):找完整的子串"abc",必须连续 3 个字符完全匹配;find_first_of("abc"):找第一个是 a/b/c 中任意一个的字符,只要匹配其中一个就停止。
#include <iostream>
#include <string>
int main () {
std::string str = "Hello123World456!" ;
size_t pos1 = str.find_first_of ("0123456789" );
if (pos1 != std::string::npos) {
std::cout << "场景 1:第一个数字在位置" << pos1 << ",字符是" << str[pos1] << std::endl;
}
size_t pos2 = str.find_first_of ("!@#$%" );
if (pos2 != std::string::npos) {
std::cout << "场景 2:第一个特殊字符在位置" << pos2 << ",字符是" << str[pos2] << std::endl;
}
size_t pos3 = str.find_first_of ("0123456789" , 6 );
if (pos3 != std::string::npos) {
std::cout << "场景 3:从 6 开始第一个数字在位置" << pos3 << ",字符是" << str[pos3] << std::endl;
}
size_t pos4 = str.find_first_of ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" );
std::cout << "场景 4:第一个非字母字符在位置" << pos4 << std::endl;
size_t pos5 = str.find_first_of ("@#$" );
if (pos5 == std::string::npos) {
std::cout << "场景 5:未找到 @#$中的任意字符" << std::endl;
}
return 0 ;
}
总结 核心作用:找第一个属于'目标字符集合'的字符(匹配任意一个即可),而非完整子串;核心区别:和 find() 比,find 匹配完整子串,find_first_of 匹配字符集合中的任意字符;核心避坑:不要把字符集合当成子串理解,判断结果用 != npos;核心场景:找第一个数字 / 字母 / 特殊符号等'特征字符'。
简单说,find_first_of() 是'字符类型匹配器',适合按字符类别(数字、字母、符号)查找,而 find() 是'完整子串匹配器'。
4. find_last_of find_last_of() 的核心是:从指定的'结束位置'开始,从右向左查找第一个属于'目标字符集合'的字符(即原字符串中最后一个匹配该集合的字符),找到返回其下标,没找到返回 std::string::npos 。
简单对比记忆:find_first_of():从左找「第一个」属于字符集合的字符;find_last_of():从右找「最后一个」属于字符集合的字符;rfind():从右找「最后一个」完整子串(需连续匹配)。
#include <iostream>
#include <string>
int main () {
std::string str = "Hello123World456!" ;
size_t pos1 = str.find_last_of ("0123456789" );
if (pos1 != std::string::npos) {
std::cout << "场景 1:最后一个数字在位置" << pos1 << ",字符是" << str[pos1] << std::endl;
}
size_t pos2 = str.find_last_of ("0123456789" , 10 );
if (pos2 != std::string::npos) {
std::cout << "场景 2:[0,10] 内最后一个数字在位置" << pos2 << ",字符是" << str[pos2] << std::endl;
}
size_t pos3 = str.find_last_of ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" );
if (pos3 != std::string::npos) {
std::cout << "场景 3:最后一个字母在位置" << pos3 << ",字符是" << str[pos3] << std::endl;
}
size_t pos4 = str.find_last_of ("!@#$%" );
if (pos4 != std::string::npos) {
std::cout << "场景 4:最后一个特殊符号在位置" << pos4 << ",字符是" << str[pos4] << std::endl;
}
size_t pos5 = str.find_last_of ("@#$" );
if (pos5 == std::string::npos) {
std::cout << "场景 5:未找到 @#$中的任意字符" << std::endl;
}
return 0 ;
}
总结 核心作用:从右往左找最后一个属于'目标字符集合'的字符,返回其下标,没找到返回 npos;核心区别:和 rfind() 比,它匹配'单个字符(集合)',而非'完整子串';和 find_first_of() 比,查找方向相反;核心避坑:不要把字符集合当成子串理解,pos 参数是'结束范围',判断结果用 != npos;核心场景:找最后一个数字 / 分隔符 / 字母等'特征字符'(如提取文件名、最后一个分隔项)。
简单说,find_last_of() 是'反向字符类型匹配器',专门解决'找最后一个某类特征字符'的需求,是处理文件路径、分隔字符串的高频工具。
5. find_first_not_of find_first_not_of() 的核心是:从指定起始位置开始,在原字符串中找第一个「不属于」目标字符集合的字符,找到返回其下标,没找到返回 std::string::npos 。
简单对比记忆:find_first_of():找「第一个属于」字符集合的字符;find_first_not_of():找「第一个不属于」字符集合的字符;比如字符集合是"0123456789",前者找第一个数字,后者找第一个非数字。
#include <iostream>
#include <string>
int main () {
std::string num_str = "12345a6789" ;
size_t pos1 = num_str.find_first_not_of ("0123456789" );
if (pos1 != std::string::npos) {
std::cout << "场景 1:第一个非数字在位置" << pos1 << ",字符是" << num_str[pos1] << std::endl;
} else {
std::cout << "场景 1:字符串全是数字" << std::endl;
}
std::string str = "###HelloWorld123!" ;
size_t pos2 = str.find_first_not_of ("!@#$%^&*()" );
if (pos2 != std::string::npos) {
std::cout << "场景 2:第一个非特殊符号在位置" << pos2 << ",字符是" << str[pos2] << std::endl;
std::string clean_str = str.substr (pos2);
std::cout << "清洗后字符串:" << clean_str << std::endl;
}
std::string space_str = " test string" ;
size_t pos3 = space_str.find_first_not_of (" " );
if (pos3 != std::string::npos) {
std::cout << "场景 3:第一个非空格字符在位置" << pos3 << ",字符是" << space_str[pos3] << std::endl;
}
std::string pure_alpha = "abcdefg" ;
size_t pos4 = pure_alpha.find_first_not_of ("abcdefghijklmnopqrstuvwxyz" );
if (pos4 == std::string::npos) {
std::cout << "场景 4:字符串全是小写字母" << std::endl;
}
return 0 ;
}
总结 核心作用:找第一个不属于目标字符集合的字符,返回其下标,没找到返回 npos;核心区别:和 find_first_of() 是'反向'逻辑(一个找'不属于',一个找'属于');核心避坑:不要混淆'属于'和'不属于',判断结果用 != npos;核心场景:字符串校验(纯数字 / 纯字母)、开头字符清洗(去空格 / 特殊符号)、非法字符检测。
简单说,find_first_not_of() 是'异常字符定位器',专门解决'找第一个不符合规则的字符'的需求,是字符串校验和清洗的高频工具。
6. find_last_not_of find_last_not_of() 的核心是:从指定的'结束位置'开始,从右向左查找第一个「不属于」目标字符集合的字符(即原字符串中最后一个不符合该集合的字符),找到返回其下标,没找到返回 std::string::npos 。
#include <iostream>
#include <string>
int main () {
std::string num_str = "12345a6789" ;
size_t pos1 = num_str.find_last_not_of ("0123456789" );
if (pos1 != std::string::npos) {
std::cout << "场景 1:最后一个非数字在位置" << pos1 << ",字符是" << num_str[pos1] << std::endl;
} else {
std::cout << "场景 1:字符串全是数字" << std::endl;
}
std::string str = "HelloWorld123!" ;
size_t pos2 = str.find_last_not_of ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" );
if (pos2 != std::string::npos) {
std::cout << "场景 2:最后一个非字母在位置" << pos2 << ",字符是" << str[pos2] << std::endl;
std::string alpha_part = str.substr (0 , pos2);
std::cout << "纯字母部分:" << alpha_part << std::endl;
}
std::string space_str = "test string " ;
size_t pos3 = space_str.find_last_not_of (" " );
if (pos3 != std::string::npos) {
std::cout << "场景 3:最后一个非空格字符在位置" << pos3 << ",字符是" << space_str[pos3] << std::endl;
std::string trim_str = space_str.substr (0 , pos3 + 1 );
std::cout << "去末尾空格后:" << trim_str << "(长度:" << trim_str.size () << ")" << std::endl;
}
std::string pure_alpha = "ABCDEFG" ;
size_t pos4 = pure_alpha.find_last_not_of ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
if (pos4 == std::string::npos) {
std::cout << "场景 4:字符串全是大写字母" << std::endl;
}
return 0 ;
}
总结 核心作用:从右往左找最后一个不属于目标字符集合的字符,返回其下标,没找到返回 npos;核心区别:和 find_first_not_of() 是'反向'逻辑(一个找最后一个不符合,一个找第一个不符合);核心避坑:不要混淆'属于'和'不属于'、pos 是结束范围、截取末尾内容要 + 1;核心场景:去除末尾空格 / 特殊符号、校验字符串末尾字符类型、提取尾部有效内容。
简单说,find_last_not_of() 是'尾部异常字符定位器',专门解决'处理字符串尾部不符合规则的字符'的需求,是字符串尾部清洗和校验的核心工具。
6. substr substr() 的核心是:从原 std::string 中截取指定范围的字符,返回一个全新的 std::string 对象 。它完全围绕 C++ 的 std::string 对象操作,不需要手动处理字符数组、不需要补 \0,是提取子串的'首选工具'。
#include <iostream>
#include <string>
using namespace std;
int main () {
string filename ("Test.cpp" ) ;
FILE* fout = fopen (filename.c_str (), "r" );
if (fout) {
cout << "打开文件成功" << endl;
fclose (fout);
string suffix = filename.substr (4 , 4 );
cout << suffix << endl;
}
return 0 ;
}
补充:npos 是 std::string 的静态常量,代表'最大无符号整数',可以理解为'直到末尾'。
总结 核心作用:从原字符串截取指定范围的字符,返回新的 std::string 对象;核心用法:substr(pos)(截到末尾)、substr(pos, count)(精准截取);核心避坑:pos 不能越界、不能传负数、返回的是独立新对象;使用原则:日常提取子串优先用 substr () ,仅需写入裸 char 数组时才用 copy ()。
简单说,substr () 是 C++ 为提取子串量身定做的'懒人工具'—— 不用管内存、不用管 \0,一行代码就能拿到想要的子串。
string 类中的一些全局函数 这里有些我们之前的内容为大家解释过了,但是我想要系统性为大家再解释一遍,这样也方便大家后续学习,不需要翻之前的博客减少负担:
1. operator+ operator+ 是 C++ 对 + 运算符的重载,专门用于字符串拼接,支持以下组合(覆盖所有常见场景):std::string + std::string、std::string + 单个字符(char)、std::string + C 风格字符串(const char*)、C 风格字符串 + std::string、单个字符 + std::string。
核心特点:拼接后返回新字符串,原字符串不会被修改 (因为是全局函数,操作的是值传递的副本)。
#include <iostream>
#include <string>
using namespace std;
int main () {
string str1 = "Hello" ;
string str2 = "World" ;
const char * c_str = " C++" ;
char ch = '!' ;
string res1 = str1 + str2;
cout << "场景 1:" << res1 << endl;
string res2 = str1 + c_str;
cout << "场景 2:" << res2 << endl;
string res3 = "Hi " + str1;
cout << "场景 3:" << res3 << endl;
string res4 = str1 + ch;
cout << "场景 4:" << res4 << endl;
string res5 = ch + str2;
cout << "场景 5:" << res5 << endl;
string res6 = str1 + " " + str2 + ch;
cout << "场景 6:" << res6 << endl;
return 0 ;
}
总结核心作用:重载 + 运算符,实现 std::string 与字符串 / 字符的拼接,返回新字符串;核心避坑:不能直接拼两个 C 风格字符串、频繁拼接优先用 append();核心特点:语法简洁、拼接安全,是少量字符串拼接的首选;核心场景:一次性拼接 2~3 个字符串 / 字符(比如拼接提示语、路径)。
简单说,operator+ 是字符串拼接的'语法糖',让代码更易读;如果追求效率,循环拼接就换成 append()。
2. getline std::getline() 的核心是:从输入流(cin / 文件流)中读取字符,直到遇到指定的终止符(默认是换行符 \n)为止,将读取的内容存入 std::string 对象(不包含终止符本身) 。
核心特点:支持读取带空格、制表符的完整一行;终止符可自定义(默认 \n);不会自动跳过开头的空白字符(和 operator>> 最大区别之一)。
#include <iostream>
#include <string>
#include <fstream>
int main () {
std::string line1;
std::cout << "请输入带空格的整行内容:" ;
std::getline (std::cin, line1);
std::cout << "场景 1 读取结果:" << line1 << std::endl;
std::string line2;
std::cout << "请输入用逗号分割的内容:" ;
std::getline (std::cin, line2, ',' );
std::cout << "场景 2 读取结果:" << line2 << std::endl;
std::ifstream file ("test.txt" ) ;
if (file.is_open ()) {
std::string file_line;
std::cout << "\n场景 3:读取文件整行:" << std::endl;
while (std::getline (file, file_line)) {
std::cout << file_line << std::endl;
}
file.close ();
}
return 0 ;
}
getline () vs operator>> 核心对比(必记) 特性 std::getline() operator>>(流提取) 终止符 默认 \n(可自定义) 任意空白字符(空格 / 制表 / 换行) 能否读取带空格内容 能(整行) 不能(仅无空格单词) 是否跳过开头空白 否(默认读取所有开头空白) 是(自动跳过开头空白) 典型场景 读取整行文本(如地址、描述) 读取无空格单词(如用户名、数值) 配合使用注意 需用 cin.ignore() 跳过残留换行 无此问题
总结 核心作用:读取输入流的整行内容(含空格),默认以 \n 终止,可自定义终止符;核心避坑:和 cin >> 配合时,必须用 cin.ignore() 跳过残留的换行符;核心对比:getline() 读'整行',operator>> 读'单词',两者互补;核心场景:读取带空格的文本(如地址、描述、整行配置)、逐行读取文件。
简单说,getline() 是处理'带空格文本输入'的核心工具,只要需要读取整行内容,优先用它,可以简单理解,cin 是输入那些完整的比如 helloworld,但是如果要输入 hello world 就要使用 getline。
operator>> 和 operator<< 分别是流插入和流提取,是运算符的重载,而 swap 比较复杂,目前大家就知道他是交换函数就好,后续内容再为大家详细解释。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online