字符串基本使用

字符串说白了就是字符类型的数组,里面可以存储字符型的数据,元素的下标从 0 开始。

在C语言风格中,使用字符数组 char[] 来操作,在C++风格中,一般使用 string 来操作,此处就着重以 string 来进行讲解。

使用 string 时,需要导入 string 或者 cstring 头文件,当然如果导入 bits/stdc++.h 万能头文件就不用管了。

1.定义字符串

格式如下所示:

string 变量名;

例如:

string s;

定义了一个字符串,变量名为 s

2.字符串输入、输出、遍历

下方的例子都以 要操作的字符串变量名是 s 为前提。

输入:如果字符串不带空格,cin >> s;,一定要注意这样输入的字符串的下标是从 0 开始的。带空格的输入方式在下方的常见技巧中进行了扩展说明。

输出:可以直接 coutcout << s;,也可以通过循环遍历来进行输出。

获取长度:一般题目中输入字符串时会给出,若没给出,可以通过 s.size() 或者 s.length() 来进行获取。这两个方法并没区别,一般为了键盘的寿命,个人更偏向使用 s.size()

需要注意的是这两个方法返回的实际上是 string::size_type 类型,是一个无符号整型,如果你需要进行长度的减法之类的操作,建议使用一个 int 型变量存储长度,使用这个变量来进行操作。

遍历:下标范围为 0~s.size()-1,所以正序遍历就是循环列出 0~s.size()-1 的数字,在循环中对 s[i] 进行操作即可。

下方这个例子,演示了字符串的简单输入与遍历输出:

#include <bits/stdc++.h>
using namespace std;
string s;  // 字符串的定义 
int len;

int main() {
	cin >> s;
	len = s.size();  // s.size() 获取这个字符串的长度,建议把长度存为带符号整数类型。
    
    cout << "直接通过cout输出:" << s << '\n';
	// 下标从 0开始,长度为 len 的字符串,0~len-1
    cout << "通过循环+下标的形式输出:";
	for (int i=0; i<len; i++) {
		cout << s[i];
	} 
	return 0;
}

3.带空格的字符串的处理

可以分成两种方式来进行输入:

需要保留空格进行处理的,可以使用 getline(cin, 字符串变量),这段代码可以把一整行的数据都输入到 s 中,包括空格。例如:

string s;
getline(cin, s);   // 假设是 hello world! hahaha 666.
cout << s;  // 输出就是 hello world! hahaha 666. 空格会保留下来。

getline 的默认终止符是 \n,也就意味着,当遇到换行时,它会终止这一次的输入。终止符也可以设置为其他字符,不过在普及组用得是不多的,这里不多做拓展。

另外要注意的是,getlinecin 如果一起用,是会出现问题的,如果你写了如下这段代码:

int n;
string s;
cin >> n;
getline(cin, s);

那么你就惨了,下面的 s 可能无法正常读入。原理这里不多赘述,解决办法就是尽量避免这样做,或者在 cingetline 之间加入一行 cin.ignore();

不需要保留空格处理的,非常典型的是 给你一段话,单词都是以空格分开,要对这些单词进行操作,而空格只是来分隔这些单词,可以使用 while + cin,例如:

string s;
while (cin >> s) {  // 假设输入的是 hello world! hahaha 666.
	cout << s << '\n';
}

这段代码会把字符串自动以空格或者换行为作为分隔符,将其他字符分别进行输入。上面这段代码的运行结果会是:

hello
world!
hahaha
666.

所以对于提取单词,这个方式是比较容易进行处理的。

需要注意的是,这种输入方式在控制台进行输入时,是不会自动结束的,它会一直等待你输入数据,如果你已经完成了输入,那么请按下 ctrl+Z (苹果用户按 command+Z),然后再按回车即可。而在代码提交到测评机上时,你不用担心它会死循环,当读入完输入文件的最后一个字符后,下一次会读到终止符,它会自动停止。

当然,也有很多其他的方式,诸如 getchar() 等,但普及组掌握以上两种输入即可,学得多了反而容易混淆。

4.课上例题

字符串的输入与输出

输出带空格的字符串

输出字符串中的指定区域

字符串常用方法&技巧

下方的例子都以 要操作的字符串变量名是 s 为前提。

1.字符筛选

获取字符串 s 的长度:s.size()

判断字符 s[i] 是否是字母isalpha(s[i]) 或者 s[i]>='A' and s[i]<='Z' or s[i]>='a' and s[i]<='z',更推荐大家理解后面的方法,因为字符存储的本质都是 ASCII 码,所以在这个 ASCII 码范围内,自然就是字母。

判断字符 s[i] 是否是数字isdigit(s[i]) 或者 s[i]>='0' and s[i]<='9'

判断字符 s[i] 是否是大写字母isupper(s[i]) 或者 s[i]>='A' and s[i]<='Z'

判断字符 s[i] 是否是小写字母islower(s[i]) 或者 s[i]>='a' and s[i]<='z'

对于上述技巧,这里有一个 DEMO 演示,推荐大家跟着一起把代码写一遍,判断大写字母和小写字母请自行尝试。

#include <bits/stdc++.h>
using namespace std;
string s;
int len;

int main() {
	cin >> s;  // 假设输入的字符串是 ab1234ABC% 
	len = s.size();  // 长度应该为 10
	cout << "长度:" << len << '\n';
	for (int i=0; i<len; i++) {  // 遍历字符串 
		// 判断是否是数字 
		if (s[i] >= '0' and s[i] <= '9') cout << "数字:" << s[i] << '\n';
		// 判断是否是字母 
		else if (isalpha(s[i])) cout << "字母:" << s[i] << '\n';
		// 都不是就是符号 
		else cout << "符号:" << s[i] << '\n';
	} 
	return 0;
}

2.大小写相关

单个字符 s[i] 小写转大写判断是小写字母 然后 s[i] -= 32 或者 直接 s[i] = toupper(s[i])

单个字符 s[i] 大写转小写判断是大写字母 然后 s[i] += 32 或者 直接 s[i] = tolower(s[i])

整个字符串中的字母全部转成小写

转小写

方式一:遍历字符串,判断是否是大写字母,是则转换成小写

方式二:transform(s.begin(), s.end(), s.begin(), ::tolower) 。这个方法可以直接把整个 s 中的字母都转成小写,并重新存储到 s 中。 如果需要存储到其他字符串中(例如 a),则把第三处的 s.begin() 改成 a.begin()

整个字符串中的字母全部转大写

转大写

方式一:遍历字符串,判断是否是小写字母,是则转换成大写

方式二:transform(s.begin(), s.end(), s.begin(), ::toupper) 。这个方法可以直接把整个 s 中的字母都转成大写,并重新存储到 s 中。 如果需要存储到其他字符串中(例如 a),则把第三处的 s.begin() 改成 a.begin()

此处进行了大小写转换的 DEMO 演示。

#include <bits/stdc++.h>
using namespace std;
string s;
int len;

int main() {
	cin >> s;  // 假设输入的字符串是 ab1234ABC% 
	len = s.size();
	for (int i=0; i<len; i++) {  // 遍历字符串 
		// 如果原本是小写字母,转为大写字母 
		if (islower(s[i])) s[i] = toupper(s[i]);
		// 如果原本是大写字母,转为小写字母 
		else if (s[i] >= 'A' and s[i] <= 'Z') s[i] += 32;
	} 
	cout << "大小写互换:" << s << '\n';  // 结果应为 AB1234abc%
	transform(s.begin(), s.end(), s.begin(), ::tolower);
	cout << "全转为小写:" << s << '\n';  // 结果应为 ab1234abc%
	transform(s.begin(), s.end(), s.begin(), ::toupper);
	cout << "全转为大写:" << s << '\n';  // 结果应为 AB1234ABC%
	return 0;
}

3.字符串处理

判断字符串 ab 是否相等
判断是否相等

方式一:遍历两个字符串然后逐个比较。

方式二:直接判断 a == b 。同样也可以用 ><等来比较字符串的大小,字符串的比较是 从第一个字符开始按位比较,如果某一位的ASCII码值不同,谁的值更大就哪个字符串更大。

字符串拼接
字符串拼接

在字符串 s 后面拼上新的字符或者别的字符串,可以直接使用 += 运算符,例如:
s += 'a';s += "abcd";s += s2; // s2指另一个字符串变量

查找子串

查找字符串 a 在 字符串 b第一次出现的位置,或者判断是否出现(即 a 是否是 b 的子串。)

查找子串

方式一:循环枚举可能的 第一次出现的位置,然后逐个判断是否每一个字符都相等

方式二:b.find(a) ,返回 ab 中第一次出现的位置,如果 a 没有在 b 中出现过,则返回 string::npos,转为有符号整数后就是 -1

下面是查找字符串的 DEMO。

#include <bits/stdc++.h>
using namespace std;
string s1, s2, s3;

int main() {
	// 假设 s1 是 abcdecde,s2 是 cde,s3 是 klm 
	cin >> s1 >> s2 >> s3;
	cout << s1.find(s2) << '\n';  // 输出为 2,因为第一次出现的位置是从 2 开始 
	cout << s1.find(s3) << '\n';  // 是一个很大的数字,因为返回的是无符号整型,转为int后其实是-1
	int flag = s1.find(s3);
	cout << flag << '\n';  // -1 
	return 0;
}
字符串截取

字符串截取,截取出字符串中的某一段。

字符串截取

方式一:循环遍历这一段字符串,比如要截取 [l,r] 这个范围内的字符串,就循环从 l 开始,到 r 结束,遍历进行操作。

方式二:利用 substr() 方法。以下是 DEMO 演示:

string s = "abcdefg", s1, s2;
s1 = s.substr(2);  // 从下标2开始,截取后面的所有字符,即cdefg
s2 = s.substr(3, 2);  // 从下标3开始,截取两个字符,即de
字符串与int转换

stringint,把一个字符串格式的数字转成 int 类型。

方式一,循环遍历,按位提取出数字,用一个整型变量来存储提取出来的数字。

string s = "114514";
int num = 0, len = s.size();
for (int i=0; i<len; i++) {
	/*
	核心为这个拼接公式,为什么num每次都要乘10呢,可以想象一下,num里面已经存了11,现在要把4拼进	 
	去,变成114,怎么拼呢,自然是 11*10+4。
	而为什么每次加 s[i] 都要减去 '0' 呢,因为字符类型本质是ASCII码值,比如字符 '0' 的ASCII码
	是48,如果要正确的把数字0拼进去,自然要减去48,其他数字同理。
	*/ 
	num = num*10 + (s[i]-'0');
}

方式二,使用 stoi ,使用时需要注意,字符串里面必须是纯数字组成的。上面那段代码可以这样写:int num = stoi(s)

intstring,把数字转成 string 类型,可以使用 to_string() 方法,例如:

int num = 114514;
string s = to_string(num);  // s = "114514"

当然这里也可以使用 数位分离+字符串拼接 来做,不过还是偏麻烦的,这里就不再演示了,感兴趣可以自行实现。

4.string的常用操作函数

红色部分为常用方法

  1. =, s.assign() // 赋以新值
  2. swap(s1, s2) // 交换两个字符串s1,s2的内容
  3. +=, s.append(), s.push_back() // 在尾部添加字符; pop_back()删除尾部字符。
  4. s.insert(i, s1) // 在s的下标i处插入字符或字符串s1
  5. s.erase(i, n) // 删除从下标i开始的n个字符
  6. s.clear() // 删除全部字符
  7. s.replace(i, n, t) // 将s从下标i开始的连续n个字符替换为t
  8. + // 串联字符串 s1 + s2
  9. ==,!=,<,<=,>,>=,compare() // 比较字符串
  10. s.size(),length() // 返回字符数量,如s1.size()
  11. s.max_size() // 返回string类型对象支持的最大字符数
  12. s.empty() // 判断字符串是否为空,空返回1,否则返回0
  13. s.capacity() // 返回重新分配之前的字符容量
  14. reserve() // 保留一定量内存以容纳一定数量的字符
  15. [], at() // 存取单一字符
  16. >>,getline() // 从stream读取某值
  17. << // 将某值写入stream
  18. copy() // 将某值赋值为一个C_string
  19. s.c_str() // 返回一个指向正规C字符串(C_string)的指针 内容与本string串相同 有 \0
  20. s.data() // 将内容以字符数组形式返回 无 \0
  21. s.substr(i, n) // 返回子字符串
  22. begin() end() // 提供类似STL的迭代器支持
  23. rbegin() rend() // 逆向迭代器
  24. count(s.begin(), s.end(), 'c') // 查找c字符在s中出现的次数
  25. s.find() //找某个子串是否在s中
  26. reverse(s.begin(),s.end()) // 反转s
  27. sort(s.begin(), s.end()) // 按字典序排序s
  28. stoi(s) //将s转为int,需保证字符串为纯数字,否则无法正确转换,返回int
  29. to_string(n) // 将int n转换为string,返回string对象

5.课上例题

输出字符串中的所有字母

统计字符串中的字符数量

字符串转大写

字符串转小写

是双胞胎吗?

判断字符串是否为回文

删除字符串中的 * 号

练习

雷同检测

验证子串

提取数字

出现次数最多的字符

最长单词

笨小猴

雷同卷

凯撒密码

找第一个只出现一次的字符

可乐的键盘

统计单词数