1. 字符与整数的联系 —— ASCII码
- 每个常用字符都对应一个-128~127的数字,二者之间可以相互转化
#include <iostream>
using namespace std;
int main()
{
char c = 'a';
cout << (int)c << endl;
int a = 66;
cout << (char)a << 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 = 'B' * 'A';
char c = 'A' + 2;
cout << a << endl;
cout << b << endl;
cout << c << endl;
return 0;
}
1.1 练习1 — 统计数字和字母个数
- 输入一行字符,统计出其中数字字符的个数,以及字母字符的个数
#include<iostream>
using namespace std;
int main()
{
char c;
int nums = 0, chars = 0;// 数字和字母个数
while(cin >> c)// 读入字符
{
if(c >= '0'&&c <= '9') nums++;
else if(c >= 'A' && c <= 'z' || c >= 'a'&& c <= 'z') chars++;
}
printf("numbers:%d\nchars:%d",nums,chars);
return 0;
}
1.2 练习2 — 字符和数字的转换
#include<iostream>
using namespace std;
int main()
{
char c = 'a';
cout << (int)c << endl;
printf("%d\n",'z'-'a');
printf("%c\n",'a'+3);
int a ='B'-'A';
char b= 'A'+2;
cout << a << endl;
cout << b << endl;
return 0;
}
1.3 练习3 — 查询ASCII码
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
for(int i = 1;i < 128;i++)
printf("%d:%c\n",i,(char)i);// 输出ASCII码以及对应字符
return 0;
}
2. 字符数组 —— 用字符数组存储字符串
字符串就是字符数组加上结束符’\0'
,string底层就是用字符数组实现的。存长度为L的字符串,则字符数组的长度至少为 L+1。- 可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个’\0’字符,
因此字符数组的长度至少要比字符串的长度多1
#include <iostream>
using namespace std;
int main()
{
// a1不是字符串,a2和a3都是字符串,因为都有'\0'
char a1[] = {'C', '+', '+'}; // 列表初始化,没有空字符
char a2[] = {'C', '+', '+', '\0'}; // 列表初始化,含有显示的空字符,有\0,可以被称为字符串
char a3[] = "C++"; // 自动添加表示字符串结尾的空字符,数组长度为4,因为有'\0'字符。
char a4[6] = "Daniel"; // 错误,没有空间可存放空字符,方框里至少填7
cout << a2 << endl;
printf("%s\n",a3);
return 0;
}
2.1 字符数组读入(fgets和getline)和输出(重点!)
- fgets()函数能读入空格
- cin >> str; 输入字符串时,遇到空格或者回车就会停止
- cout << str << endl; 输出字符串时,遇到空格或者回车不会停止
- printf(“%s\n”,str); 等价于 puts(str);puts函数输出包括换行符,printf输出不包含
#include<iostream>
using namespace std;
int main()
{
char str[100];
cin >> str; // 输入字符串时,遇到空格或者回车就会停止
cout << str << endl; // 输出字符串时,遇到空格或者回车不会停止
printf("%s\n",str);// 等价于 puts(str);puts函数后面包括换行符
return 0;
}
- printf(“%s\n”,str); 等价于 puts(str),但是puts函数后面包含换行符
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
char str[100];
scanf("%s",str);
// 等价于 puts(str);puts函数包括换行符
printf("%s\n",str);
return 0;
}
- 读入一行字符串,包括空格,不能再使用gets函数,已经被淘汰了。以下是读入有空格的句子
- 方式1:使用fgets()函数
#include <iostream>
using namespace std;
int main()
{
char str[100];
fgets(str,100,stdin);// stdin是系统定义好的变量
cout << str << endl;
return 0;
}
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
void print(char str[])
{
// puts(str);// 调用函数直接打印,会输出回车
printf("%s",str);
}
int main()
{
char str[110];
// 多输入回车,中间的是101
fgets(str,101,stdin);// 在字符串最后加回车"\n",使用puts函数会输出回车,printf不会输出回车
// 或者直接用getline
cin.getline(str, 101);// getline函数和fgets函数二者选择其中一个
print(str);
return 0;
}
- 方式2:使用getline函数——只适用于string类型
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str;
getline(cin, str);// 只能是string类型
cout << str << endl;
return 0;
2.1.1 练习1 — 字符串输出
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char a1[] = {'A','B','C'};
cout << a1 + 1 << endl;// 输出BC
// 从A开始输出, cout << a1 << endl
// 从B开始输出, cout << a1 + 1 << endl
char a2[] = {"ABCDEF"};
printf("%s\n",a2 + 2);// 输出cdef
}
2.1.2 练习2 — 字符串读入
遇到空格、回车、结束符就停止读入
(重难点)- scanf(“%s”,a)一定不要加&
所有数组的名字本身就是一个指针
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char s[100];
// scanf("%s",s); 输入abc
// cout << s << endl;// 输出abc
cin >> s;// 同scanf
cout << s << endl;
return 0;
}
- 从指定数组位置开始输入和输出数据
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char s[100];
// 读入字符串是读到空格或者回车为止。
cin >> s+1;// 从s[1]开始读入字符串,就是从s[1]开始存入字符串abc,相配套的是cout << s+1,从s[1]开始输出字符串。
// scanf("%s",s+1);// 不要用取地址符号&,或者scanf("%s",&s[1])也是可以的,这里也是从s[1]开始存入字符串,同上面的 cin >> s+1
cout << s+1 << endl;// 从s[1]开始输出完整字符串,abc
cout << s[1] << endl; // 数组下标从1开始,结果为a。
// printf("%s\n",s + 1); 表示从下表1开始输出。等同于cout << s+1 <<endl
return 0;
}
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
char s[100];
scanf("%s",s + 2);
cin >> s + 2;
printf("%s", s + 1);
cout << s + 2 << endl;
return 0;
}
2.2 字符数组的常用操作(#include<cstring>)
- 下面几个函数需要引入头文件:#include <string.h>
- (1) strlen(str),求字符串的长度,只计算字符串中的元素,不包括\0
- (2) strcmp(a, b),比较两个字符串的大小,a < b 返回-1,a == b 返回0,a >
b返回1。这里的比较方式是字典序
! - (3) strcpy(a, b),将字符串b复制给从a开始的字符数组
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
char a[100] = "hello world!", b[100];
cout << strlen(a) << endl;// 不包括'\0'字符
strcpy(b, a);// 复制
return 0;
}
- strcpy复制函数
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
char s1[100], s2[100];
scanf("%s",s1);
strcpy(s2, s1);// 读入s1复制给s2
cout << s2 << endl;
return 0;
}
- strcmp 比较函数
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
char a[100], b[100];
scanf("%s%s",a,b);
cout << strcmp(a,b) << endl;// 输入时两个字符串需要用空格或者回车分开
return 0;
}
- 遍历每一个字符
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
char s1[100], s2[100];
scanf("%s",s1);
int len = strlen(s1);// 提高效率
for(int i = 0; i < len; i++) cout << s1[i] << endl;
return 0;
}
2.2.1 练习1 — 字符串(char数组)长度strlen()
- char 数组求长度用strlen函数, int 数组用siezof(a)/sizeof(a[0]), string类型用size()函数
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
// 第一种方法
char s[100];
scanf("%s",s);
cout << strlen(s) << endl;// 长度
return 0;
// 第二种方法
char s[100];
scanf("%s",s);
int len = 0
for(int i = 0; s[i] ;i++) len++;// s[i]表示不等于零的情况下,i++
cout << len << endl;
}
2.2.2 练习2 — 字符串比较strcmp()
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char s1[100],s2[100];
scanf("%s%s",s1,s2);
cout << strcmp(s1,s2) << endl;// 字符串的比较
return 0;
}
2.2.3 练习3 — 字符串复制strcpy()
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char s1[100],s2[100];
scanf("%s",s1);
strcpy(s2,s1);
cout << s2 << endl;//字符串复制
return 0;
}
2.2.4 练字4 — 遍历字符数组中字符strlen()
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char a[100] = "hello world!";
// for(int i = 0;i < strlen(a);i++) cout << a[i] << endl;// 这种写法效率低
for(int i = 0, len = strlen(a);i < len; i++) cout << a[i] << endl;
return 0;
}
2.3 练习1 —— Acwing 772.只出现一次的字符
-
题目要求:给定一个只包含小写字母的字符串,请你找到第一个仅出现一次的字符。如果没有,输出“no”。
-
思路:如何读入字符串?如何遍历字符串中每个字符?如何统计每个字符出现的次数?
-
如何统计字母出现的次数:开一个cnt[26]数组
-
方法1
#include<iostream>
#include<cstdio>
#include<cstring>// 字符串的函数
using namespace std;
int cnt[26] = {0};// 存储每个字母出现的次数
char str[100010];// 存储字符串
int main()
{
cin >> str;
// str[i]里面都是小写字母,小写字母 - 'a'结果是整数,0-25分别表示a-z,'a'在做运算时都是整数运算
for(int i = 0;i < strlen(str);i ++) cnt[str[i] - 'a'] ++;// cnt[]++表示对当前数组元素进行自增操作,元素值+1。数组加上下标就是变量
// strlen每次进行循环都会重新执行一遍,延长执行的时间
for(int i = 0; i < strlen(str);i ++)
if(cnt[str[i] - 'a'] == 1)// 判断cnt[]中哪一个的元素值为1
{
cout << str[i] << endl;// 只出现一次则输出字符
return 0;
}
puts("no");// 没有则输出no
return 0;
}
方法2:使用len = strlen(str),可以缩短运行时间
#include<iostream>
#include<cstdio>
#include<cstring>// 字符串的函数
using namespace std;
int cnt[26] = {0};// 存储每个字母出现的次数
char str[100010];// 存储字符串
int main()
{
cin >> str;
int len = strlen(str);
// str[i]里面都是小写字母,小写字母 - 'a'结果是整数,0-25分别表示a-z,'a'在做运算时都是整数运算
for(int i = 0;i < len;i ++) cnt[str[i] - 'a'] ++;// cnt[]++表示对当前数组元素进行自增操作,元素值+1。数组加上下标就是变量
// strlen每次进行循环都会重新执行一遍,延长执行的时间
for(int i = 0; i < len;i ++)
if(cnt[str[i] - 'a'] == 1)// 判断cnt[]中哪一个的元素值为1
{
cout << str[i] << endl;
return 0;
}
puts("no");
return 0;
}
- 方法3:不求str的长度,条件用str[i],表示遇到’\0’就停止。字符串结尾是\0
#include<iostream>
#include<cstdio>
#include<cstring>// 字符串的函数
using namespace std;
int cnt[26] = {0};// 存储每个字母出现的次数
char str[100010];// 存储字符串
int main()
{
cin >> str;
// str[i]里面都是小写字母,小写字母 - 'a'结果是整数,0-25分别表示a-z,'a'在做运算时都是整数运算
for(int i = 0;str[i];i ++) cnt[str[i] - 'a'] ++;// cnt[]++表示对当前数组元素进行自增操作,元素值+1。数组加上下标就是变量。等价于 cnt[] += 1
for(int i = 0;str[i];i ++)
if(cnt[str[i] - 'a'] == 1)// 判断cnt[]中哪一个的元素值为1
{
cout << str[i] << endl;
return 0;
}
puts("no");
return 0;
}
2.4 练习2 —— Acwing 769.替换字符
- 把一个字符串中特定的字符全部用给定的字符替换,得到一个新的字符串
难点
- scanf(“\n%c”, &c);读入要替换的字符,读入字符不会自动过滤掉前面的回车\n,会读入回车。需要在前面加\n
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
char str[31];
scanf("%s",str);// 读入到回车或者'\0'就结束了
char c;// 读入要替换的字符
scanf("\n%c", &c);// 读入要替换的字符,读入字符不会自动过滤掉前面的回车\n,会读入回车。需要在前面加\n,过滤回车。cin 没有这个问题
for(int i = 0;str[i];i++)
if(str[i] == c)
str[i] = '#';
puts(str);
return 0;
}
3. 标准库类型 string —— 用string来存储字符串
- 可变长的字符序列,比字符数组更加好用。需要引入头文件:#include <cstring>
3.1 定义和初始化
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
string s1; // 默认初始化,s1是一个空字符串
string s2 = s1; // s2是s1的副本
string s3 = "hiya";// s3是该字符串字面值的副本
string s4(10, 'C'); // s4内容是ccccccccccc
return 0;
}
3.2 string 的操作
3.2.1 string的读入和输出(重点)
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1,s2;
cin >> s1 >> s2;// 读入
// scanf("%s",&s1);是错误的写法,不能用scanf读入字符串
cout << s1 << ' ' << s2 << endl;
// 输出可以用printf,
printf("%s\n",s1.c_str());// c_str()是成员函数,返回的是存储字符串s1的字符数组的首地址,打印出的结果是字符数组。cin输入abc,打印出来的也是abc
// 输出也可以用puts
puts(s1.c_str());
return 0;
}
- 注意:不能用printf直接输出string,需要写成:printf(“%s”, s.c_str())
3.2.2 string读入一行使用getline(重点!)
- 如果输入是:abc cde abd,使用 cin >> s,则输出是abc,使用 getline(cin, s)则 输出是abc cde abd。
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s1;
getline(cin, s1);// string读入一行
// cin >> s1; // 表示读入第一个字符串
// cin.getline(s2, 1000);// 字符串使用cin读入一行
cout << s1 << endl;
return 0;
}
3.2.3 string的empty和size(求长度)函数
- 注意size是无符号整数,因此 s.size() <= -1一定成立。
- empty()返回的是bool值
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s1, s2 = "abc";
cout << s1.empty() << endl;// empty返回的是布尔值
cout << s2.empty() << endl;
cout << s2.size() << endl;
return 0;
}
3.2.4 string的比较
- 支持 > < >= <= == !=等所有比较操作,按字典序进行比较
3.2.5 string对象赋值
string s1(10, ‘c’), s2; // s1的内容是 cccccccccc;s2是一个空字符串
s1 = s2; // 赋值:用s2的副本替换s1的副本
// 此时s1和s2都是空字符串
3.2.6 两个string对象相加
string s1 = “hello, ”, s2 = “world\n”;
string s3 = s1 + s2; // s3的内容是 hello, world\n
s1 += s2; // s1 = s1 + s2
3.2.7 字面值和string对象相加
- 做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来。
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s1 = "hello", s2 = "world"; // 在s1和s2中都没有标点符号
string s3 = s1 + ", " + s2 + '\n';
cout << s3 <<endl;
return 0;
}
- 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string。
string s4 = s1 + ","; // 正确:把一个string对象和有一个字面值相加
string s5 = "hello" + ","; // 错误:两个运算对象都不是string
string s6 = s1 + "," + "world"; // 正确,每个加法运算都有一个运算符是string。s1 + ","结果是string类型
string s7 = "hello" + "," + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的。先计算"hello" + ","得到字符串,不是string类型
3.3 处理string对象中的字符(重点!)
3.3.1 数组长度(char-strlen&string-size)+引用&
- 可以将string对象当成字符数组来处理
char字符数组长度用strlen()
string字符串长度使用size()
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
// char a[100] = "hello world!";
// for(int i = 0;i < strlen(a);i++) cout << a[i] << endl;
string s = "hello world";
for(int i = 0;i < s.size();i ++) cout << s[i] << ' ';
return 0;
}
- 或者使用基于范围的for语句
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s = "hello world";
for(char c : s) cout << c << endl;// c++的范围遍历,char 表示每一个字符数组元素的类型,s表示字符串,c顺次遍历s[0],s[1],...
cout << endl;
cout << s << endl;
for(char &c : s) c = 'a';// 改变c的同时改变s的值,加了&表示c等价于字符串s
cout << s << endl;
return 0;
}
int main()
{
string s = "hello world";
for(char c : s)
{
c = 'a';
}
// 等价于下面的操作
for(int i = 0; i < s.size();i ++)
{
char c = str[i];
c = 'a';
}
}
- 加引用符号&
int main()
{
string s = "hello world";
for(char &c : s)
{
c = 'a';
}
// 等价于下面的操作
for(int i = 0; i < s.size();i ++)
{
char &c = str[i];// 加引用符号&后,c和str完全相同
c = 'a';
}
}
3.3.1 auto语法
- auto让编译器自己猜数据类型
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s = "hello world";
for(auto c : s) cout << c << endl;// c++的范围遍历,char 表示每一个字符数组元素的类型,s表示字符串,c顺次遍历s[0],s[1],...
return 0;
}
3.3.2 练习1 —— Acwing 767. 信息加密
- 密码翻译,输入一个只包含小写字母的字符串,将其中的每个字母替换成它的后继字母,如果原字母是’z’,则替换成’a’。
- 思考:读入包括空格的字符串(使用getline函数)
#include<iostream>
using namespace std;
int main()
{
string s;
getline(cin, s);// 读入有空格的字符串
for(auto &c : s)
{
if(c >= 'a' && c <= 'z') c = 'a' + (c - 'a' + 1) % 26;// 后面表示新的偏移量
else if(c >= 'A' && c <= 'Z') c = 'A' + (c - 'A' + 1) % 26;
}
cout << s << endl;
return 0;
}