第五讲 字符串¶
字符串是计算机与人类沟通的重要手段。——闫学灿
已经在数学上证明了程序可以实现任意的逻辑
1. 字符与整数的联系——ASCII码¶
每个常用字符都对应一个-128 ~ 127的数字,二者之间可以相互转化。注意:目前负数没有与之对应的字符。
#include <iostream>
using namespace std;
int main()
{
char c = 'a';
cout << (int)c << endl;
int a = 66;
cout << (char)97 << endl;
return 0;
}
(重要)常用ASCII值:'A'- 'Z'是65 ~ 90,'a' - 'z'是97 - 122,0 - 9是 48 - 57。 字符可以参与运算,运算时会将其当做整数:
#include <iostream>
using namespace std;
int main()
{
int a = 'B' - 'A';
int b = 'A' * 'B';
char c = 'A' + 2;
cout << a << endl;
cout << b << endl;
cout << c << endl;
return 0;
}
2. 字符数组¶
字符串就是字符数组加上结束符\0
可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个'\0'字符,因此字符数组的长度至少要比字符串的长度多 1!
#include <iostream>
using namespace std;
int main()
{
char a1[] = {'C', '+', '+'}; // 列表初始化,没有空字符
char a2[] = {'C', '+', '+', '\0'}; // 列表初始化,含有显示的空字符
char a3[] = "C++"; // 自动添加表示字符串结尾的空字符
char a4[6] = "Daniel"; // 错误:没有空间可以存放空字符
return 0;
}
2.1 字符数组的输入输出:¶
#include <iostream>
using namespace std;
int main()
{
char str[100];
cin >> str; // 输入字符串时,遇到空格或者回车就会停止
cout << str << endl; // 输出字符串时,遇到空格或者回车不会停止,遇到'\0'时停止
printf("%s\n", str); // 注意字符串本身就是地址,不需要加&
return 0;
}
读入一行字符串,包括空格:
#include <iostream>
using namespace std;
int main()
{
char str[100];
fgets(str, 100, stdin); // gets函数在新版C++中被移除了,因为不安全。
// 可以用fgets代替,但注意fgets不会删除行末的回车字符
// 100表示输入最多多少个字符
cout << str << endl;
return 0;
}
string 的话要用get line(cin, s)]
Char s[100]的话要用cin.getline(s, 100)
Puts(s) 等价于print("%s\n", s), 注意包括了换行符
2.2 字符数组的常用操作¶
下面几个函数需要引入头文件:
#include <string.h>
或者#include <cstring>
(1) strlen(str),求字符串的长度
(2) strcmp(a, b),比较两个字符串的大小,a < b返回-1,a == b返回0,a > b返回1。这里的比较方式是字典序!
(3) strcpy(a, b),将字符串b复制给从a开始的字符数组。注意顺序!
字典序,从头开始,比ASCII码,比如abc < abde 10 < 2(一位一位的比较)strcmp返回的是-1
字典序本身具有贪心的性质,所以多和贪心相关
strlen是不包含\0的
回车一般是刷新缓存,所以按了回车之后,会将缓存中的东西传递给程序
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char a[100] = "hello world!", b[100];
cout << strlen(a) << endl; // 12
strcpy(b, a);
cout << strcmp(a, b) << endl; //0
return 0;
}
2.3遍历字符数组中的字符¶
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s1[100], s2[100];
scanf("%s", s1);
for (int i = 0, len = strlen(s1); i < len; i++) cout << s1[i] << endl;
return 0;
}
3. 标准库类型string¶
可变长的字符序列,比字符数组更加好用,需要引入头文件:
#include <string>
3.1 定义和初始化¶
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1; // 默认初始化,s1是一个空字符串
string s2 = s1; // s2是s1的副本,注意s2只是与s1的值相同,并不指向同一段地址
string s3 = "hiya"; // s3是该字符串字面值的副本
string s4(10, 'c'); // s4的内容是 "cccccccccc"
return 0;
}
3.2 string上的操作¶
(1) string的读写
#include <iostream>
#include <string.h>
using namespace std;
int main () {
string a;
cin >> a;
// cout << a << endl;
printf("%s", a.c_str());
return 0;
}
注意:不能用scanf("%s", &s)来读入string
注意:不能用printf直接输出string,需要写成:printf(“%s”, s.c_str());
(2) 使用getline读取一整行
#include <iostream>
#include <string>
using namespace std;
int main () {
string a;
getline(cin, a);
cout << a << endl;
return 0;
}
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int main()
{
char s[100]; // 能一直读入,包括空格
cin.getline(s, 100); // 读入字符组成的字符串
// 引入string
string s1, s2;
cin >> s1; // 只能读入第一个字符串,遇到空格和回车会直接停止
getline (cin, s2); // 前面写cin,后面写读入的字符串,会直接开始读入,哪怕第一个字符是空格
cout << s << endl << s1 << endl << s2 << endl;
return 0;
}
(3) string的的empty和size操作(注意size是无符号整数,因此 s.size() <= -1一定成立)?????
empty告诉我们字符串是不是空的,空的就是true(0),否则是false(1)
string的size功能的时间复杂度是O1,比strlen好多了!
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1, s2 = "abc";
cout << s1.empty() << endl; // 1
cout << s2.empty() << endl; // 0
cout << s1.size() << endl; // 0
cout << s2.size() << endl; // 3
return 0;
}
(4) string的比较
支持 >, <, >=, <=, ==, !=等所有比较操作,按字典序
进行比较。
(5) 为string对象赋值
string s1(10, 'c'), s2; // s1的内容是 cccccccccc;s2是一个空字符串
s1 = s2; // 赋值:用s2的副本替换s1的副本
// 此时s1和s2都是空字符串
(6) 两个string
对象相加
string s1 = "hello, "", s2 = "world\n";
string s3 = s1 + s2; // s3的内容是 hello, world\n
s1 += s2; // s1 = s1 + s2
(7) 字面值和string对象相加:
做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来:
string s1 = "hello", s2 = "world"; // 在s1和s2中都没有标点符号
string s3 = s1 + ", " + s2 + '\n';
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string:
普通字符串和string不是一个东西!
string s4 = s1 + ", "; // 正确:把一个string对象和有一个字面值相加
string s5 = "hello" + ", "; // 错误:两个运算对象都不是string
string s6 = s1 + ", " + "world"; // 正确,每个加法运算都有一个运算符是string
string s7 = "hello" + ", " + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的
3.3 处理string对象中的字符¶
可以将string
对象当成字符数组来处理:
#include <iostream>
#include <string>;
using namespace std;
int main () {
string s = "hello, world!";
for (int i = s.size() - 1; i >= 0; i--) cout << s[i];
return 0;
}
或者使用基于范围的for 语句:
这个叫C++的范围遍历,for (char c : s),前面是字符数组里每个字符的类型
注意得到的东西是,string里面复制过来的值,所以不能用:
for (char c : s) {
c = 'a';
}
而要用
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "Hello, world!";
for (char c: s) cout << c;
cout << endl;
for (char &c: s) c = 'a';
cout << s;
return 0;
}
cpp中还提供auto
自动识别类型
比如:
for (auto &c : c) {
c = 'a'
}
后面复杂起来之后用auto会非常简单
注意:fgets会把末尾的回车读进来
删掉最后一个字符:
s.pop_back();
取字符串:
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main () {
string a, b;
while (cin >> a >> b) {
int p = 0;
for (int i = 0; i < a.size(); i++) {
if (a[i] > a[p]) {
p = i;
}
}
cout << a.substr(0, p + 1) + b + a.substr(p + 1) << endl;
}
return 0;
}
760. 字符串长度¶
#include <iostream>
#include <string>
using namespace std;
int main () {
string s;
getline(cin, s); // 注意getline输入一整行
printf("%d", s.size());
return 0;
}
772. 只出现一次的字符¶
char在运算中会被自动转化为数字
#include <iostream>
#include <cstring>
using namespace std;
char str[100010];
int cnt[26];
int main () {
scanf("%s", str);
for (int i = 0; str[i]; i++) {
cnt[str[i] - 'a']++;
}
for (int i = 0; str[i]; i++) {
if (cnt[str[i] - 'a'] == 1) {
cout << str[i] << endl;
return 0;
}
}
puts("no");
return 0;
}
听到1.5字符串匹配1:47:32