Kodewick
第 5 课,共 30 课目录

字符串

第 5 课,共 30 课 · 第 1 部分 · 起步 · 约 15 分钟

名字、地址、商品编号……文字在程序里到处都是。C++ 用 std::string 放一串文字(要 #include <string>)。两个字符串用 + 就能接起来:

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string city = "上海";6    std::string street = "南京路";7    std::string address = city + street + " 100 号";8    std::cout << address << '\n';9    address += ",3 楼";10    std::cout << address << '\n';11    return 0;12}
输出
上海南京路 100 号
上海南京路 100 号,3 楼

+ 把几段文字连成一个新字符串;+= 把一段文字接到原来那个的后面,和第 4 课里数字的 += 一个意思。

长度 size()

s.size() 告诉你字符串有多长。.size() 写在变量后面,是字符串自己带的成员函数,以后还会见到很多这样的写法。

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string code = "AB-1024";6    std::string name = "牛奶";7    std::cout << code.size() << '\n';8    std::cout << name.size() << '\n';9    return 0;10}
输出
7
6

AB-1024 是 7 个字符,显示 7。可是「牛奶」两个字显示的是 6:size() 数的是字节,英文字母、数字、符号各占 1 个字节,一个汉字在这里占 3 个。所以这一课下面按位置取字符的例子都用英文和数字。

用下标取字符

字符串里的每个字符都有一个编号,叫下标,从 0 开始。s[0] 是第一个字符,s[s.size() - 1] 是最后一个。取出来的是 char。std::string 是可以改的:给 s[i] 赋值,就换掉了那个字符。

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string name = "jack";6    std::cout << name[0] << '\n';7    std::cout << name[name.size() - 1] << '\n';8    name[0] = 'J';9    std::cout << name << '\n';10    return 0;11}
输出
j
k
Jack

点单步,看 name 从 jack 变成 Jack。注意 'J' 是单引号:name[0] 是一个字符,放进去的也得是一个字符。

下标越界

"AB12" 只有 4 个字符,下标是 0 到 3。用 s[10] 去取一个不存在的字符,是未定义行为:普通的电脑上可能显示一个乱七八糟的字符,也可能出别的事。我们的引擎会停在这一行:

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string code = "AB12";6    std::cout << code[3] << '\n';7    std::cout << code[10] << '\n';8    return 0;9}
输出
2
index out of range

发生了什么: 停在第 7 行:你访问了 string[10],可它只有 4 个字符(下标 0~3)。

code[3] 是最后一个字符 2,没问题;code[10] 越界了。(C++ 倒是允许读 code[4]:它是字符串结尾一个看不见的空字符,对你没什么用。)用下标之前,先想一想它是不是小于 size()。

substr 和 find

s.substr(start, n) 从下标 start 开始取 n 个字符,组成一个新字符串;不写 n 就一直取到最后。s.find("x") 找 x 第一次出现的下标。两个配起来,就能把一个邮箱拆成两半:

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string email = "[email protected]";6    auto at = email.find("@");7    std::cout << at << '\n';8    std::cout << email.substr(0, at) << '\n';9    std::cout << email.substr(at + 1) << '\n';10    return 0;11}
输出
3
lin
example.com

@ 在下标 3。substr(0, 3) 是前 3 个字符 lin,substr(4) 是从下标 4 到最后。find 返回的类型名字很长,这里正好用 auto。

找不到的时候,find 返回一个特别的值 std::string::npos(「没有这个位置」):

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string email = "lin.example.com";6    auto at = email.find("@");7    std::cout << (at == std::string::npos) << '\n';8    return 0;9}
输出
1

显示 1,表示「是真的」:没找到。== 这一句外面要加括号,不然 << 会先算。第 6 课学了 if,就能写「找到了怎样,找不到怎样」。

比较字符串

== 看两个字符串是不是一模一样,< 和 > 按字典的顺序比,从第一个字符开始一个一个比:

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string a = "apple";6    std::string b = "banana";7    std::cout << (a == "apple") << '\n';8    std::cout << (a < b) << '\n';9    std::cout << ("Zoo" < a) << '\n';10    return 0;11}
输出
1
1
1

三行都是 1。最后一行可能让你意外:比的是字符的编码,所有大写字母都排在小写字母前面,所以 Zoo 比 apple 小。汉字也是按编码比,不是按拼音或笔画。

先猜一猜

这段程序显示什么?

1#include <iostream>2#include <string>3 4int main() {5    std::string a = "12";6    std::string b = "3";7    std::cout << a + b << '\n';8    return 0;9}

数字和字符串互换

要把数接进字符串,先用 std::to_string 把它变成字符串;反过来,std::stoi 把一串数字变成 int(i 是 int 的意思):

main.cpp
1#include <iostream>2#include <string>3 4int main() {5    int count = 3;6    std::string msg = "购物车里有 " + std::to_string(count) + " 件商品";7    std::cout << msg << '\n';8    std::string text = "42";9    int n = std::stoi(text);10    std::cout << n + 8 << '\n';11    return 0;12}
输出
购物车里有 3 件商品
50

text + "8" 会是 "428";std::stoi(text) + 8 才是 50。从输入读到的、从文件里拿到的数字常常是字符串,算之前要先转换。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。

练习 1

拼出全名

用 + 把 last 和 first 接起来,显示 林小明,你好!。

要显示
林小明,你好!
exercise1.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string first = "小明";6    std::string last = "林";7 8    return 0;9}
输出
点「运行」或按 Ctrl+Enter
练习 2

邮箱里的用户名

用 find 和 substr 取出 @ 前面的部分,显示 用户名:zhang.wei。

要显示
用户名:zhang.wei
exercise2.cpp
1#include <iostream>2#include <string>3 4int main() {5    std::string email = "[email protected]";6 7    return 0;8}
输出
点「运行」或按 Ctrl+Enter
练习 3

修好它:文字加数字

这段程序编译不过。改好它,让它显示 共 5 件。

要显示
共 5 件
exercise3.cpp
1#include <iostream>2#include <string>3 4int main() {5    int count = 5;6    std::string msg = "共 " + count + " 件";7    std::cout << msg << '\n';8    return 0;9}
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • std::string 放一串文字;+ 把字符串接起来,+= 接到后面。
  • s.size() 是字节数:英文字符各占 1 个,汉字在这里各占 3 个。
  • s[i] 取出下标 i 的字符,下标从 0 开始;字符串可以改:s[0] = 'J';。
  • 下标越界(i 不小于 size())是未定义行为,引擎会停下。
  • s.substr(start, n) 取一段;s.find("x") 找位置,找不到是 std::string::npos。
  • ==、< 比较字符串,按字符的编码一个一个比,大写字母排在小写前面。
  • std::to_string(n) 把数变成字符串,std::stoi(s) 把字符串变成 int。