【C++】掌握string类操作:高效处理字符串
1. string 类对象的修改操作
函数名称 | 功能说明 |
push_back | 在字符串的末尾插入单个字符 c |
append | 在字符串的末尾追加一个完整的字符串(支持多种参数形式,如const string&、C 风格字符串等) |
operator+= | (重点)在字符串的末尾追加内容,支持追加单个字符、string对象或 C 风格字符串(语法简洁,如str += "abc") |
c_str | (重点)返回指向字符串的 C 风格字符数组指针(以'\0'结尾),用于兼容 C 语言接口 |
find + npos | (重点)从字符串的pos位置(默认从 0 开始)向后查找字符或子串,返回第一次出现的位置;若未找到,返回string::npos(一个特殊的静态常量,表示未找到) |
rfind | 从字符串的pos位置(默认从末尾开始)向前查找字符或子串,返回最后一次出现的位置;若未找到,返回string::npos |
substr | 从字符串的pos位置开始,截取n个字符(若n超出剩余字符数,则截取到字符串末尾),返回截取得到的子串 |
注意:
在 string 尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下 string 类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
对 string 操作时,如果能够大概预估到放多少字符,可以先通过 reserve 把空间预留好。
void test1()
{string url("https://cplusplus.com/reference/string/basic_string/substr/");size_t i1 = url.find(":");if (i1 != string::npos){cout << url.substr(0, i1) << endl;}size_t i2 = url.find("/",i1+3);if (i2 != string::npos){cout << url.substr(i1 + 3, i2-(i1+3)) << endl;}cout << url.substr(i2 + 1) << endl;}int main()
{test1();return 0;
}
find()
用于查找特定字符的位置substr(pos, length)
用于截取子字符串,当省略 length 时,会截取从 pos 到字符串末尾的部分string::npos
是一个特殊值,表示查找失败(未找到指定字符)
1.1 find()
函数:查找字符 / 子串位置
功能:在字符串中查找指定字符或子串的第一个出现位置。
size_t find(const string& str, size_t pos = 0) const; // 查找子串str
size_t find(char c, size_t pos = 0) const; // 查找字符c
参数说明:
str
/c
:要查找的子串或字符。pos
(可选):从字符串的第pos
个位置开始查找(默认从 0 开始)。
返回值:
找到时:返回匹配的第一个字符的索引位置(
size_t
类型)。未找到时:返回
string::npos
(表示查找失败)。
string s = "hello world";
size_t pos1 = s.find('o'); // 查找字符'o',返回4(第一个'o'在索引4)
size_t pos2 = s.find("world"); // 查找子串"world",返回6
size_t pos3 = s.find('x'); // 未找到,返回string::npos
1.2substr()
函数:截取子字符串
功能:从字符串中截取指定范围的子串。
string substr(size_t pos, size_t count = npos) const;
参数说明:
pos
:子串的起始位置(必须在字符串范围内,否则会抛出异常)。count
(可选):要截取的字符个数。若省略或超过剩余字符数,则截取从pos
到字符串末尾的所有字符。
返回值:
截取得到的子串(
string
类型)。
示例:
string s = "abcdefgh";
string sub1 = s.substr(2, 3); // 从索引2开始,截取3个字符 → "cde"
string sub2 = s.substr(5); // 从索引5开始,截取到末尾 → "fgh"
string sub3 = s.substr(3, 10); // 从索引3开始,截取10个字符(实际只有5个)→ "defgh"
1.3 string::npos
:表示 “未找到” 或 “到末尾” 的特殊值
本质:string::npos
是std::string
类的静态成员常量,类型为size_t
(无符号整数),通常定义为-1
(但由于是无符号类型,实际表示为该类型的最大值)。
作为find()
等查找函数的 “未找到” 标志。
if (s.find("test") == string::npos) {cout << "未找到子串" << endl;
}
1.4三者结合的典型用法
通常用find()
查找位置,用string::npos
判断是否找到,再用substr()
截取子串,例如解析 URL 的逻辑:
string url = "https://cplusplus.com/reference/";
size_t pos = url.find("://"); // 查找"://"的位置
if (pos != string::npos) { // 若找到string protocol = url.substr(0, pos); // 截取协议部分("https")string rest = url.substr(pos + 3); // 截取剩余部分("cplusplus.com/...")
}
1.5 string 类非成员函数
函数 / 运算符名称 | 功能说明 |
operator+ | 用于拼接两个字符串(或字符串与字符),返回新的字符串对象。由于采用传值返回,会触发深拷贝,效率较低,实际开发中建议优先使用 operator+= 替代。 |
operator>> | (重点)输入运算符重载,用于从输入流(如 cin)读取字符串,默认以空白字符(空格、回车、制表符等)为分隔符,自动跳过前导空白。 |
operator<< | (重点)输出运算符重载,用于将字符串内容输出到输出流(如 cout)。 |
getline | (重点)从输入流中读取一整行字符串(包括空格),直到遇到换行符 '\n' 为止,换行符会被读取但不包含在结果中,常用于读取带空格的完整字符串。 |
relational operators | (重点)关系运算符重载,包括 ==、!=、<、>、<=、>=,用于字符串之间的大小比较(按字符的 ASCII 码值逐位比较,类似字典序)。 |
上面的几个接口大家了解一下,下面的 OJ 题目中会有一些体现他们的使用。string 类中还有一些其他的
操作,这里不一一列举,大家在需要用到时不明白了查文档即可。
1.6vs 和 g++下 string 结构的说明
注意:下述结构是在 32 位平台下进行验证,32 位平台下指针占 4 个字节。
vs 下 string 的结构
string 总共占 28 个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string 中字
符串的存储空间:
当字符串长度小于 16 时,使用内部固定的字符数组来存放
当字符串长度大于等于 16 时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger one value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符串有效长度
引用计数
struct _Rep_base
{ size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount;
};
指向堆空间的指针,用来存储字符串