CC++ & Algorithm

string的基本操作:构造、拼接与遍历

中等7
语言版本:通用
概述:学习如何创建字符串、连接多个字符串以及逐个字符地访问字符串中的每个元素,就像用积木搭火车一样简单。

字符串基础:构造、拼接与遍历——像搭积木火车一样简单

什么是字符串?为什么需要操作它?

字符串就是一连串字符排成的队伍,比如你的名字 "小明"、一句歌词 "Twinkle twinkle little star",甚至一个密码 "abc123"。在编程中,字符串是最常用的数据类型之一——你想存储聊天消息、处理用户输入、生成文件名,都离不开它。

C++ 的 std::string 和 Python 的 str 都提供了方便的操作来构造、拼接和遍历。这就好比你有了一盒字母积木,想怎么拼就怎么拼。

生活中的类比:积木火车

想象你有一盒字母积木:

  • 构造:从空盒子开始,或者直接摆好一列固定的积木(比如 "Hello")。
  • 拼接:把两列积木首尾相连,变成一列更长的火车。
  • 遍历:从火车头走到火车尾,看看每个车厢里是什么字母。

编程里的字符串就是这列积木火车,每个字符是一个车厢。


1. 构造:创建字符串(搭好火车车厢)

为什么需要不同的构造方式?

有时候你刚开始手里什么都没有(空字符串),有时候已经知道火车的内容(比如 "Hello"),有时候想用同一个字母重复很多遍(比如 "AAAAA")。不同的构造方式让你更灵活地准备数据。

C++ 中的构造

  • 默认构造string s1;——创建一个空箱子,里面没有字母。
  • C风格字符串构造string s2("Hello");——把已有的字母序列放进箱子。
  • 重复字符构造string s3(5, 'A');——一次放入 5 个字母 'A',得到 "AAAAA"。
  • 拷贝构造string s4(s2);——复制一份一模一样的火车,s4 也有 "Hello"。

生活例子:老师让你准备班级点名册,你可以:

  • 拿一个空白本子(默认构造)
  • 用打印好的名单直接订上(C字符串构造)
  • 全班都姓“王”,写 5 个“王”字(重复字符构造)
  • 复印一份别的班的花名册(拷贝构造)

Python 中的构造

Python 更随意:

  • 直接赋值s = "hello"(引号里写什么就是什么)
  • str() 转换s = str(123)"123"(把数字变成字符串)
  • 重复字符'A' * 5"AAAAA"(乘号就是重复)
  • 空字符串s = ""(一对空引号)

注意:Python 字符串一旦创建就不能修改,所以赋值操作其实是换了一列新火车(后面的拼接会详细说)。


2. 拼接:连接字符串(把火车连起来)

为什么需要拼接?

你想把名字和祝福语连起来:"小明" + "生日快乐" → "小明生日快乐"。或者把多个单词拼成一个句子。

C++ 中的拼接

C++ 的 string 是可变的,可以直接用运算符或成员函数修改自身:

  • + 运算符string s5 = s2 + " " + s3;
    这不会改变 s2 和 s3,而是造一列新火车。就像把两列积木火车分开搬,然后首尾接在一起。

  • += 运算符s5 += "!";
    直接在原有火车末尾加一节车厢,火车变长了,原来的 s5 被改写了。

  • append() 成员函数s6 = s2.append(" World");
    效果和 += 类似,更明确地表示“追加”。注意:append() 会修改 s2 本身并返回引用,所以 s6 和 s2 指向同一列火车(不过这里 s6 是新变量接收了返回值,但 s2 也被改了)。

Python 中的拼接

Python 字符串不可变,所以每次 ++= 都创建新字符串,旧字符串被丢弃。

  • + 运算符s5 = s2 + " " + s3
    把 s2、空格、s3 三列火车拼成一列,但 s2 和 s3 仍然原地不动。

  • += 运算符s5 += "!"
    看起来是追加,实际上是:先把 s5 的内容和 "!" 拼成一个新字符串,再把 s5 指向这个新字符串。原来的 s5 对象被销毁。

  • ''.join() 方法(推荐大量拼接):

    parts = ["Hello", "World"]
    s6 = " ".join(parts)  # 用空格连接列表里的字符串,效率高
    

    这好比把一堆积木车厢按顺序串起来,而不是一个个地粘。

新手容易犯的错误:在 Python 里用循环 s = s + ch 拼接大量字符,每次循环都创建新字符串,速度慢且浪费内存。正确的做法是先把字符放列表里,最后用 join


3. 遍历:逐个访问字符(检阅火车车厢)

为什么要遍历?

你要检查每个字母是不是大写、统计有多少个元音、或者打印每个字符……遍历让你能挨个处理车厢里的字母。

C++ 中的遍历

  • 下标访问s[i]
    就像火车车厢编号从 0 开始。注意 i 必须小于字符串长度(s.size()),否则越界,程序可能崩溃。

  • 范围 for 循环(C++11起)for (char ch : s)
    最简洁的方式,自动从头到尾拿到每个字符。相当于你从车头走到车尾,每节车厢看一眼,但不知道车厢编号。

  • 使用迭代器for (auto it = s.begin(); it != s.end(); ++it)
    迭代器就像一个小指针,你手动控制每一步。*it 就是当前指向的字符。

Python 中的遍历

  • 直接 for ch in s:和 C++ 范围 for 一样简洁。
  • range 和下标for i in range(len(s)) 然后 s[i],可以同时得到索引和字符。
  • enumeratefor i, ch in enumerate(s) 同时拿到索引和字符,最方便。

生活例子:老师查全班作业,可以:

  • 按学号一个一个叫(下标)
  • 直接按座位顺序收(范围 for)
  • 一边叫学号一边喊名字(enumerate)

新手容易犯的错误:在 C++ 中直接用 s[i] 却不检查下标是否越界。比如循环条件写成 i <= s.size(),导致最后一次索引等于长度,越界访问。记住 s.size() 返回的是字符个数,最大下标是 size()-1


C++ 完整代码示例(带详细注释)

#include <iostream>
#include <string>
using namespace std;

int main() {
    // --------------------------------------------
    // 1. 构造:准备各种字符串
    // --------------------------------------------
    string emptyStr;                     // 空字符串,没有字符
    string helloStr("Hello");            // 从C风格字符串构造,内容是"Hello"
    string repeatedStr(5, 'A');          // 重复5个'A',得到"AAAAA"
    string copyStr(helloStr);            // 拷贝构造,与helloStr内容相同

    cout << "emptyStr: [" << emptyStr << "]" << endl;
    cout << "helloStr: " << helloStr << endl;
    cout << "repeatedStr: " << repeatedStr << endl;
    cout << "copyStr: " << copyStr << endl;

    // --------------------------------------------
    // 2. 拼接:连接字符串
    // --------------------------------------------
    string joined = helloStr + " " + repeatedStr;  // 用+连接,不会修改helloStr和repeatedStr
    cout << "joined after +: " << joined << endl;  // "Hello AAAAA"

    joined += "!";                                 // 用+=在末尾追加,joined本身被修改
    cout << "joined after +=: " << joined << endl; // "Hello AAAAA!"

    // append函数:直接将参数追加到调用对象末尾,并返回引用
    string appended = helloStr.append(" World");   // helloStr变成"Hello World", appended也是"Hello World"(因为append返回引用)
    cout << "helloStr after append: " << helloStr << endl;
    cout << "appended (same as helloStr): " << appended << endl;

    // --------------------------------------------
    // 3. 遍历:逐个访问字符
    // --------------------------------------------
    string train = "ABCDE";

    // 方法一:下标遍历(小心越界)
    cout << "下标遍历: ";
    for (int i = 0; i < (int)train.size(); i++) {  // 注意size()返回无符号,最好转成int或使用size_t
        cout << train[i] << " ";
    }
    cout << endl;

    // 方法二:范围for循环(最推荐,简洁安全)
    cout << "范围for遍历: ";
    for (char ch : train) {
        cout << ch << " ";
    }
    cout << endl;

    // 方法三:迭代器遍历
    cout << "迭代器遍历: ";
    for (auto it = train.begin(); it != train.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

Python 等价功能的代码示例(带详细注释)

# --------------------------------------------
# 1. 构造:创建字符串
# --------------------------------------------
empty_str = ""               # 空字符串
hello_str = "Hello"          # 直接赋值
repeated_str = 'A' * 5       # 重复字符,得到 "AAAAA"
copy_str = hello_str         # 直接赋值(Python里是引用,但字符串不可变,所以修改不会影响原对象)

print(f"empty_str: [{empty_str}]")
print(f"hello_str: {hello_str}")
print(f"repeated_str: {repeated_str}")
print(f"copy_str: {copy_str}")

# --------------------------------------------
# 2. 拼接:连接字符串
# --------------------------------------------
joined = hello_str + " " + repeated_str   # 用+连接,原字符串不变
print(f"joined after +: {joined}")        # "Hello AAAAA"

joined += "!"                              # += 创建新字符串,再赋值给joined
print(f"joined after +=: {joined}")        # "Hello AAAAA!"

# 大量拼接推荐用 join
parts = ["Hello", "World"]
joined_parts = " ".join(parts)            # 用空格连接列表中的字符串
print(f"joined_parts from join: {joined_parts}")  # "Hello World"

# --------------------------------------------
# 3. 遍历:逐个访问字符
# --------------------------------------------
train = "ABCDE"

# 方法一:直接 for 循环(最常用)
print("直接for遍历:", end=" ")
for ch in train:
    print(ch, end=" ")
print()

# 方法二:用 range + 下标
print("下标遍历:", end=" ")
for i in range(len(train)):
    print(train[i], end=" ")
print()

# 方法三:enumerate 同时获取索引和字符
print("enumerate遍历:", end=" ")
for i, ch in enumerate(train):
    print(f"({i},{ch})", end=" ")
print()

常见错误与避坑指南

错误类型错误示例说明正确做法
C++ 下标越界for (int i=0; i<=s.size(); i++) 使用 s[i]字符串长度为 size(),最大下标是 size()-1条件改为 i < s.size()
Python 拼接性能s = ''; for ch in many_chars: s += ch每次 += 都创建新字符串,复杂度 O(n²)先用列表收集,最后 ''.join(list)
C++ 混淆 ++=以为 s1 + s2 会修改 s1+ 返回新对象,不改变原字符串想改变原字符串用 +=append
Python 字符串可变性误以为可以 s[0] = 'a'Python 字符串不可变,不能通过下标修改改用列表或 s.replace 创建新字符串

总结与下一步

  • 构造:创建字符串的不同方式,让你能应对各种初始需求。
  • 拼接:把多个字符串连起来,注意 C++ 可变与 Python 不可变的区别。
  • 遍历:逐个字符处理,C++ 推荐范围 for,Python 推荐直接 for 或 enumerate。

掌握了这些基础操作,你就已经能处理大部分常见任务了。接下来可以学习:

  • 查找子串:在字符串里找某个单词(如 findindex
  • 截取子串:取出一段(如 substr、切片)
  • 替换与大小写转换:修改内容(replacetoupper
  • 字符串流:用 ostringstreamjoin 高效拼接大量字符串

还记得吗?字符串就像积木火车,你现在已经会搭车厢、连成串、再一一看过它们。继续玩下去,你会搭出更酷的火车!

例题精讲

1单选题

下列选项中,哪种方式不能用于构造一个 std::string 对象?

Astd::string s;
Bstd::string s(10, 'a');
Cstd::string s = "Hello";
Dstd::string s(42);
2单选题

现有 std::string a = "Hello"; std::string b = "World"; 下列哪个表达式可以正确拼接得到 "HelloWorld"?

Aa + b
Ba - b
Ca * b
Da / b
3判断题

使用下标运算符[]访问 std::string 中的字符时,如果下标超出字符串长度,程序会抛出 std::out_of_range 异常。

4填空题
以下代码使用范围for循环遍历字符串并输出每个字符,请填空完成:

std::string str = "GESP";
for (char ch : ___) {
    std::cout << ch << " ";
}
5填空题
请完成以下代码,将两个字符串拼接后赋值给第三个字符串:

std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = ___ ;
std::cout << s3;  // 输出 HelloWorld