C++从入门到实战(十七)String(下)详细讲解String的常用接口(string修饰函数,处理字符串的成员函数,npos常量与string类非成员函数)_string npos
C++从入门到实战(十七)String(下)详细讲解String的常用接口
- 前言
- 一、string的修饰函数
-
- 1. 追加内容(Append)
- 2. 赋值操作(Assign)
- 3. 插入操作(Insert)
- 4. 删除操作(Erase)
- 5. 替换操作(Replace)(谨慎使用,效率极低!!!)
- 6. 交换操作(Swap)
- 对比表格
- 二、string类中 npos 常量
-
- 1. npos的定义与含义
- 2. npos的常见用途
-
- (1)查找失败的返回值
- (2)指定“直到字符串末尾”
- (3)构造子串时的默认长度
- 3. 为什么用npos而不是直接用-1?
- 4. 注意事项
- 三、String 处理字符串的成员函数
-
- 1. 查找操作:find()
- 2. 反向查找:rfind()
- 3. 子串提取:substr()
- 4. 复制到字符数组:copy()
- 5. 字符串比较:compare()
- 总结
- 四、string类非成员函数
-
- 1. operator+尽量少用
- 2. operator>> 输入运算符重载
- 3. operator<< 输出运算符重载
- 4. getline() 获取一行字符串
- 5. 关系运算符(relational operators)
- 总结
前言
- 在上一篇博客中,我们探讨了
string
实战的部分接口,涵盖构造接口、析构接口、迭代器、遍历修改、容量管理以及数据访问等内容。 - 本篇博客将延续
string
实战的主题,聚焦于解析string
类的常用接口,助力读者深入理解其核心用法与实践技巧。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
C++string的官方讲解网站
https://cplusplus.com/reference/string/string/?kw=string
一、string的修饰函数
1. 追加内容(Append)
operator+=(重要)
- 作用:直接在字符串末尾添加内容。
- 示例:
string s = \"Hello\";s += \" World\"; // s = \"Hello World\"s += \'!\'; // s = \"Hello World!\"
append()
- 作用:追加字符串、子串或重复字符。
- 示例:
string s = \"Hello\";s.append(\" World\"); // s = \"Hello World\"s.append(\"12345\", 2); // 追加前2个字符 → s = \"Hello World12\"s.append(3, \'!\'); // 追加3个\'!\' → s = \"Hello World12!!!\"
push_back()
- 作用:在字符串末尾添加单个字符。
- 示例:
string s = \"abc\";s.push_back(\'d\'); // s = \"abcd\"
2. 赋值操作(Assign)
assign()(实践中不太常用)
- 作用:替换字符串内容,支持多种赋值方式。
- 示例:
string s = \"old\";s.assign(\"new\"); // s = \"new\"s.assign(\"12345\", 3); // 取前3个字符 → s = \"123\"s.assign(5, \'a\'); // 重复5个\'a\' → s = \"aaaaa\"
3. 插入操作(Insert)
insert()
- 作用:在指定位置插入内容。
- 示例:
string s = \"Hello World\";s.insert(6, \"Beautiful \"); // 在位置6插入 → s = \"Hello Beautiful World\"s.insert(0, 2, \'!\'); // 在开头插入2个\'!\' → s = \"!!Hello Beautiful World\"
4. 删除操作(Erase)
erase()
- 作用:删除指定位置的字符或子串。
- 示例:
string s = \"Hello World\";s.erase(5); // 删除位置5及之后的字符 → s = \"Hello\"s.erase(2, 2); // 从位置2开始删除2个字符 → s = \"Heo\"
pop_back()
- 作用:删除字符串的最后一个字符(C++11起)。
- 示例:
string s = \"abc\";s.pop_back(); // s = \"ab\"
5. 替换操作(Replace)(谨慎使用,效率极低!!!)
replace()
-
作用:替换指定位置的子串。
-
示例:
string s = \"Hello World\";s.replace(6, 5, \"Universe\"); // 从位置6开始的5个字符替换为\"Universe\"// s = \"Hello Universe\"
-
为什么效率极低?
-
对于字符串替换,每次调用replace()都可能引发内存重新分配,尤其是在字符串变长的时候。
-
若进行全局替换(如替换所有匹配项),由于需要多次遍历字符串,时间复杂度会达到 O (n²)。
-
建议谨慎使用替换操作,特别是在处理大规模数据的时候。
6. 交换操作(Swap)
swap()
- 作用:交换两个字符串的内容(高效操作,常数时间复杂度)。
- 示例:
string s1 = \"apple\";string s2 = \"banana\";s1.swap(s2); // s1 = \"banana\", s2 = \"apple\"
对比表格
operator+=
s += \"test\";
append()
s.append(\"abc\", 2);
→ s += \"ab\"
push_back()
s.push_back(\'!\');
assign()
s.assign(\"new\", 2);
→ s = \"ne\"
insert()
s.insert(0, \"pre\");
→ \"pre\" + s
erase()
s.erase(1, 2);
→ 删除位置1开始的2个字符replace()
s.replace(0, 3, \"ABC\");
swap()
s1.swap(s2);
pop_back()
s.pop_back();
二、string类中 npos 常量
一句话理解
npos
就像字符串世界里的“无限远”或“找不到”:
- 当查找失败时,它告诉你“没找到”;
- 当指定范围时,它帮你“直达字符串终点”。
1. npos的定义与含义
定义:
static const size_t npos = -1;
- 类型:
size_t
(无符号整数,通常是unsigned long long
)。 - 值:
-1
,但由于无符号类型,实际表示为该类型的最大值(如64位系统为18446744073709551615
)。
作用:
- 表示“不存在”或“直到末尾”:在字符串操作中,用于标记查找失败或指定范围到字符串末尾。
2. npos的常见用途
(1)查找失败的返回值
- 场景:当
find()
、rfind()
等函数未找到目标时,返回npos
。 - 示例:
string s = \"hello\";size_t pos = s.find(\"world\");if (pos == string::npos) { cout << \"未找到\" << endl; // 输出此行}
(2)指定“直到字符串末尾”
- 场景:在
substr()
、erase()
等函数中,若长度参数为npos
,表示操作到字符串末尾。 - 示例:
string s = \"hello world\";// 从位置6开始截取到末尾string sub = s.substr(6); // 等价于 s.substr(6, string::npos) → \"world\"// 删除从位置5开始的所有字符s.erase(5); // 等价于 s.erase(5, string::npos) → \"hello\"
(3)构造子串时的默认长度
- 场景:在子串构造函数中,若未指定长度,默认截取到末尾。
- 示例:
string s = \"hello world\";string sub(s, 6); // 从位置6开始截取到末尾 → \"world\"
3. 为什么用npos而不是直接用-1?
- 类型安全:
npos
是size_t
类型,与字符串函数的返回值类型匹配,避免隐式类型转换导致的问题。 - 代码可读性:
npos
直观表达“不存在”或“末尾”的语义,比-1
更易理解。
4. 注意事项
-
比较时必须用
==
:// 正确if (s.find(\"x\") == string::npos) {...}// 错误(无符号数比较)if (s.find(\"x\") < 0) {...} // 永远不成立!
-
避免数值运算:
npos
是极大的无符号数,参与运算可能导致意外结果。
size_t x = string::npos - 10; // 结果是一个极大的数,而非-10
三、String 处理字符串的成员函数
1. 查找操作:find()
作用:在字符串中查找指定内容的第一次出现位置。
常用重载形式:
- 查找字符串:
find(const string& str, size_t pos = 0)
- 查找字符:
find(char c, size_t pos = 0)
示例:
string s = \"hello world\";// 查找字符串size_t pos1 = s.find(\"world\"); // pos1 = 6(\"world\"的起始位置)// 从位置5开始查找字符\'o\'size_t pos2 = s.find(\'o\', 5); // pos2 = 7(第二个\'o\'的位置)// 未找到时返回string::npos(通常是-1,但类型为size_t)if (s.find(\"xyz\") == string::npos) { cout << \"未找到\" << endl;}
2. 反向查找:rfind()
作用:在字符串中查找指定内容的最后一次出现位置(从后往前找)。
示例:
string s = \"hello world\";// 查找最后一个\'l\'的位置size_t pos = s.rfind(\'l\'); // pos = 9
3. 子串提取:substr()
作用:从原字符串中截取子串。
语法:
string substr(size_t pos = 0, size_t len = npos) const;
pos
:起始位置(默认为0)。len
:子串长度(默认为npos
,表示截取到字符串末尾)。
示例:
string s = \"hello world\";// 从位置6开始截取3个字符string sub1 = s.substr(6, 3); // sub1 = \"wor\"// 从位置6开始截取到末尾string sub2 = s.substr(6); // sub2 = \"world\"
4. 复制到字符数组:copy()
作用:将字符串的部分内容复制到字符数组中。
语法:
size_t copy(char* dest, size_t len, size_t pos = 0) const;
dest
:目标字符数组。len
:复制的最大字符数。pos
:起始位置(默认为0)。
示例:
string s = \"hello\";char buffer[10];// 从位置1开始复制3个字符到buffersize_t copied = s.copy(buffer, 3, 1); // copied = 3,buffer内容为\"ell\"buffer[copied] = \'\\0\'; // 手动添加字符串结束符
5. 字符串比较:compare()
作用:比较两个字符串的大小(字典序)。
常用重载形式:
int compare(const string& str) const; // 与另一个string比较int compare(const char* s) const; // 与C风格字符串比较
- 返回值:
0
:相等。- 正数:当前字符串 > 参数。
- 负数:当前字符串 < 参数。
示例:
string s1 = \"apple\";string s2 = \"banana\";if (s1.compare(s2) < 0) { cout << \"s1 < s2\" << endl; // 输出此行}// 更简单的比较方式(直接用运算符)if (s1 < s2) { cout << \"s1 < s2\" << endl; // 效果同上}
总结
find()
s.find(\"abc\")
→ 返回\"abc\"的起始位置,未找到返回string::npos
rfind()
s.rfind(\'x\')
→ 返回最后一个’x’的位置substr()
s.substr(2, 3)
→ 从位置2开始截取3个字符(如s=\"hello\"
→ \"ell\"
)copy()
s.copy(buffer, 5, 0)
→ 复制前5个字符到buffer
(需手动添加\\0
)compare()
s1.compare(s2)
→ 等价于s1 < s2
(但返回值为整数)注意事项
string::npos
:所有查找函数未找到目标时返回该值,判断时需用==
。substr()
越界:若pos + len
超过字符串长度,自动截取到末尾。copy()
无结束符:复制后需手动添加\\0
才能作为C风格字符串使用。compare()
vs 运算符:直接用==
、<
等运算符更直观,推荐使用。
四、string类非成员函数
1. operator+尽量少用
为什么?
operator+
用于拼接字符串,但它的效率较低。因为它采用传值返回,每次使用都会创建新的string
对象,涉及深拷贝(复制整个字符串内容),尤其在循环中频繁拼接时,性能损耗明显。
示例(低效写法):
string result;for (int i = 0; i < 1000; i++) { result = result + \"a\"; // 每次都创建新对象,慢!}
替代方案:
使用+=
或append()
,它们直接在原字符串后追加,避免频繁深拷贝。
string result;for (int i = 0; i < 1000; i++) { result += \"a\"; // 高效:直接追加}// 或用 append()result.append(\"abc\");
2. operator>> 输入运算符重载
作用:
从输入流(如键盘)读取字符串,遇到空格或换行符停止。
示例:
#include #include using namespace std;int main() { string name; cout << \"请输入姓名:\"; cin >> name; // 输入 \"John Doe\",实际只读取 \"John\" cout << \"你好,\" << name << endl; return 0;}
注意:
operator>>
会自动跳过开头的空格。- 遇到空格后停止读取,适合读取单个单词。
3. operator<< 输出运算符重载
作用:
将字符串输出到流(如屏幕)。
示例:
string message = \"Hello, World!\";cout << message << endl; // 输出:Hello, World!
进阶:
可与其他流操作符连用:
int age = 20;cout << \"我今年\" << age << \"岁了。\" << endl; // 输出:我今年20岁了。
4. getline() 获取一行字符串
作用:
读取整行文本,包括空格,直到遇到换行符。
基本语法:
getline(cin, str); // 从标准输入读取一行到 str
示例:
string address;cout << \"请输入地址:\";getline(cin, address); // 输入 \"北京市海淀区\",完整读取cout << \"你的地址是:\" << address << endl;
注意:
- 如果之前使用过
cin
读取其他数据(如数字),需用cin.ignore()
清除缓冲区的换行符:int num;string line;cin >> num; // 输入数字后按回车,换行符留在缓冲区cin.ignore(); // 清除换行符getline(cin, line); // 此时能正确读取下一行
5. 关系运算符(relational operators)
作用:
比较两个字符串的大小,基于字典序(即字母表顺序)。
常用运算符:
==
、!=
、<
、>
、<=
、>=
示例:
string a = \"apple\";string b = \"banana\";cout << (a == b) << endl; // 输出 0(false)cout << (a < b) << endl; // 输出 1(true),因为 \'a\' < \'b\'cout << (a > \"apple\") << endl; // 输出 0(false),内容相同
进阶:
比较规则:
- 逐字符比较 ASCII 码值。
- 若某字符不同,该位置字符大的字符串更大。
- 若所有字符相同,长度长的字符串更大。
\"apple\" < \"banana\" // true(\'a\' < \'b\')\"apple\" < \"apples\" // true(长度不同)\"apple\" > \"Apples\" // true(小写 \'a\' 比大写 \'A\' 大)
总结
operator+
s = s1 + s2
+=
替代operator>>
cin >> s
operator<<
cout << s
getline()
getline(cin, s)
s1 == s2
、s1 < s2
以上就是这篇博客的全部内容,下一篇我们将继续探索STL中String里更多精彩内容。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482