C++ 字符串反转

反转字符串,指的就是把字符串里的字符从后往前倒过来排列,比如将 "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' 不需要交换,循环正常结束。

平时如果遇到要求手写反转逻辑的练习题,小伙伴们按照上面这种 “首尾双下标向中间靠拢” 的思路来写就可以了。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号