反转字符串,指的就是把字符串里的字符从后往前倒过来排列,比如将 "Hello" 变成 "olleH"。
在 C++ 中,反转 std::string 字符串一般分为 2 种情况:一种是直接修改原有的字符串变量,另一种是保留原字符串不变并生成一个新的反转字符串。下面我们就来看看具体的实现方式。
使用 std::reverse() 函数(推荐)
如果我们希望直接把原有的 std::string 变量反转过来,不需要保留原来的字符顺序,调用 <algorithm> 头文件中的 std::reverse() 函数是最简单直接的做法。
语法:
#include <algorithm>
std::reverse(str.begin(), str.end());说明:
其中,reverse 在英文中是 “反转、颠倒” 的意思。调用时传入的 str.begin() 和 str.end() 用来标记字符串的起始与末尾位置,它们在 C++ 中统称为迭代器。后面章节会详细介绍迭代器,这里我们先当作固定搭配使用即可。
需要注意的是,std::reverse() 会直接在原字符串内部颠倒字符顺序,该函数本身没有返回值。
示例 1:原地反转字符串
#include <algorithm>
#include <iostream>
#include <string>
int main() {
std::string text = "lvyenet";
std::reverse(text.begin(), text.end());
std::cout << text << '\n';
return 0;
}运行结果如下。
teneyvl分析:
在这个例子中,text.begin() 和 text.end() 把整个字符串的范围传给了 std::reverse()。执行完毕后,原变量 text 保存的内容直接从 "lvyenet" 变成了 "teneyvl"。
初学的小伙伴第一次使用 std::reverse() 时,很容易误以为它会返回一个新字符串,于是使用等号去接收返回值。下面这种写法就是错误的,编译时会直接报错:
std::string text = "Hello";
std::string result = std::reverse(text.begin(), text.end());这是因为 std::reverse() 直接修改的是原字符串 text 本身,函数返回类型为 void,也就是不返回任何值,所以不能写在等号右边赋给变量。
注意 std::reverse() 是按单个 char 字节倒序的,只适用于英文、数字和英文符号。在 UTF-8 编码下汉字占 3 个字节,直接按单字节反转会导致乱码。
使用 rbegin() 和 rend() 生成新字符串
在有些场景下,我们并不希望改动原来的字符串,而是想另外生成一个新的反转字符串。比如检验一个单词正着读和反着读是否一样时,就需要保留原字符串,再与反转后的结果进行字符串比较。
遇到这种需求时,我们可以使用 std::string 自带的 rbegin() 和 rend(),直接创建出反转后的新字符串。
语法:
std::string 新字符串(str.rbegin(), str.rend());说明:
rbegin() 和 rend() 开头的字母 r 代表 “reverse(反向)”,表示从原字符串 str 的末尾往开头倒着读取字符。在新变量名后面使用小括号传入这对反向位置,既不需要引入 <algorithm> 头文件,也不会改动原字符串。
示例 2:生成新的反转字符串
#include <iostream>
#include <string>
int main() {
std::string text = "Hello";
std::string reversed(text.rbegin(), text.rend());
std::cout << text << '\n';
std::cout << reversed << '\n';
return 0;
}运行结果如下。
Hello
olleH分析:
在这个例子中,reversed(text.rbegin(), text.rend()) 会把 text 里的字符从尾到头依次读出来,生成新字符串 "olleH"。与此同时,原来的 text 依然保持 "Hello" 不变。
如果想在不修改原字符串的前提下得到反转结果,下面 2 种方式是等价的:
std::string text = "Hello";
// 方式 1(推荐)
std::string rev1(text.rbegin(), text.rend());
// 方式 2
std::string rev2 = text;
std::reverse(rev2.begin(), rev2.end());其中,方式 1 一行代码就能直接生成反转后的新字符串,比先复制一份副本再调用 std::reverse() 更加简洁,在实际开发中我们更推荐使用方式 1。
使用双下标循环手动反转(了解)
平时做基础编程练习时,有时题目会要求 “不使用现成的库函数,自己写循环把字符串反转过来”。
手动反转的思路其实很好理解:我们先通过 size() 函数获取字符串长度,再定义 left 和 right 两个整型下标变量,分别指向首字符(下标 0)和尾字符(下标 len - 1)。
接着配合 while 循环,在每一轮中交换 left 和 right 位置的字符,并让 left 往右走一步、right 往左走一步,直到它们在中间相遇为止。
示例 3:双下标循环反转字符串
#include <iostream>
#include <string>
int main() {
std::string text = "ABCDE";
int len = text.size();
int left = 0;
int right = len - 1;
while (left < right) {
char temp = text[left];
text[left] = text[right];
text[right] = temp;
left++;
right--;
}
std::cout << text << '\n';
return 0;
}运行结果如下。
EDCBA分析:
上面代码先把 text.size() 存入有符号整型变量 len 中,再计算 right = len - 1。这样就算遇到空字符串 "",算出的 right 也是安全的 -1,不会发生无符号整数 0 - 1 的回绕错误。
以长度为 5 的 "ABCDE" 为例,其有效下标为 0 ~ 4,循环内部的执行过程如下:
- 第 1 次循环:
left为 0,right为 4,借助临时变量temp交换首尾的'A'和'E',字符串变为"EBCDA"。接着left加 1 变为 1,right减 1 变为 3。 - 第 2 次循环:
left为 1,right为 3,交换'B'和'D',字符串变为"EDCBA"。接着left变为 2,right也变为 2。 - 退出循环:此时
left和right都等于 2,条件left < right不再成立,正中间的字符'C'不需要交换,循环正常结束。
平时如果遇到要求手写反转逻辑的练习题,小伙伴们按照上面这种 “首尾双下标向中间靠拢” 的思路来写就可以了。
