主页 > 互联网  > 

C++蓝桥杯基础篇(七)

C++蓝桥杯基础篇(七)
片头

嗨~小伙伴们,大家好!今天我们来一起学习蓝桥杯基础篇(七),学习相关字符串的知识,准备好了吗?咱们开始咯!


一、字符与整数的联系——ASCII码

每个常用字符都对应一个-128~127的数字,二者之间可以相互转化:

int main() { char c = 'a'; cout << (int)c << endl; int a = 66; cout << (char)a << endl; return 0; }

打印结果为:

97

B

 常用的ASCII值:'A'~'Z'是65~90,'a'~'z'是97~122,'0'~'9'是48~57,字符可以参与运算,运算时会将其当做整数:

// 字符可以参与运算,运算时会将其当做整数 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; }

运行结果为:

1

4290

C

 练习:输入一行字符,统计出其中数字字符的个数,以及字母字符的个数

//练习: 输入一行字符,统计出其中数字字符的个数,以及字母字符的个数 int main() { char c; int num = 0; //数字字符的个数 int chars = 0; //字母字符的个数 while (cin >> c) { if (c >= '0' && c <= '9') num++; else if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') chars++; } cout << "数字字符的个数为: " << num << endl; cout << "字母字符的个数为: " << chars << endl; return 0; }
二、字符数组

字符串就是字符数组加上结束符'\0'。

可以使用字符串来初始化字符数组,但是要注意,每个字符串结尾会暗含一个'\0'字符,因此字符数组的长度至少要比字符串的长度多1!

int main() { char a1[] = { 'C','+','+' }; //没有'\0' char a2[] = { 'C','+','+','\0' }; //有'\0' char a3[] = "C++"; //自动添加表示字符串结尾的空字符'\0' //char a4[6] = "Daniel";//没有位置存放'\0',报错 char a4[7] = "Daniel"; //正确写法 cout << sizeof a1 << endl; //3 cout << sizeof a2 << endl; //4 cout << sizeof a3 << endl; //4 return 0; }

如果我们想访问从字符数组的第2个位置开始,或者从中间位置开始,那么我们可以这样:

int main() { char a2[] = { 'A','B','C','D','\0' }; char a3[] = "ABCDEF"; cout << a2 + 1 << endl; //BCD printf("%s\n", a3 + 2); //CDEF return 0; }
2.1  字符数组的输入输出 // 2.1 字符数组的输入输出 int main() { char str[100]; char s[100]; scanf("%s", s); //这里不能加&,数组名代表首元素的地址 cin >> str; //输入字符串时,遇到空格或者回车就会停止 cout << str << endl; //输出字符串时,遇到空格或者回车不会停止 printf("%s\n", str); return 0; }

读入一行字符串,包括空格

//读入一行字符串,包括空格 int main8() { char str[100]; gets(str); cout << str << endl; return 0; }

 虽然用 gets() 时有空格也可以直接输入,但是 gets() 有一个非常大的缺陷,即它不检查预留存储区是否能够容纳实际输入的数据。

换句话说,如果输入的字符数目大于数组的长度,gets 无法检测到这个问题,就会发生内存越界,所以编程时建议使用 fgets()。

fgets()函数原型为:

char *fgets(char *s, int size, FILE *stream);

从stream流中读取size个字符存储到字符指针变量s所指向的内存空间

返回值为一个指针,指向字符串中第一个字符的地址

1.  s:代表要保存到内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名

2. size:代表的是读取字符串的长度

3. stream:表示从何种流中读取,可以是标准输入流stdin,也可以是文件流

 因此,我们可以使用fgets()来帮助我们读取字符串

int main() { char str[100]; //从输入流stdin中读取100个字符到字符数组str中。 fgets(str, 100, stdin); cout << str << endl; return 0; }

我们还可以使用cin来读取string类型的字符串,cin输入接触到第一个非空格字符时开始阅读,当遇到下一个字符(空格、制表符、换行符)时,就会停止读取

int main() { string s; printf("输入:\n"); cin >> s; printf("输出:\n"); cout << s; return 0; }

 为了避免cin对于空白符的忽略问题,我们可以使用getline函数,getline函数可以读取整行,包括前面和中间的空格,并将其存储在字符串对象中,在输入时,直至遇到'\n'或EOF,才终止一行字符串的输入操作。

getline()函数有4种重载形式,这里我们先介绍1种,后续慢慢讲解

istream& getline(istream& is, string& str);

读取的istream是作为参数 is 传进函数的,读取的字符串保存在string类型的str中。

is : 表示一个输入流,例如cin。

str : 为string类型的引用,用来存储输入流中的流信息。

因此,我们可以使用getline()函数来读取字符串

int main() { string s; getline(cin, s); cout << s << endl; return 0; }
2.2  字符数组的常用操作

下面几个函数需要引入头文件 #include<string.h>

(1)strlen(str),求字符串的长度

(2)strcmp(a,b),比较2个字符串的大小,a<b返回-1,a==b返回0,a>b返回1。这里的比较方式就是字典序!

(3)strcpy(a,b),将字符串b复制给从a开始的字符数组

下面我们依次来介绍这些函数

①strlen(str),求字符串的长度

// (1)strlen(str),求字符串的长度 int main() { char a[100] = "hello world!"; cout << strlen(a) << endl; // 12 return 0; }

②strcmp(a,b),比较2个字符串的大小

// (2)strcmp(a,b),比较2个字符串的大小, // a<b时,返回-1;a==b时,返回0;a>b时,返回1 int main() { char s1[100], s2[100]; scanf("%s%s", s1, s2); //scanf函数的%s,会自动忽略空格,换行,Tab键 cout << strcmp(s1, s2) << endl; return 0; }

或者

int main() { char s1[100]; scanf("%s", s1); cout << strcmp(s1, "abc") << endl;//将s1字符数组里面的内容和"abc"进行比较 return 0; }

③strcpy(a,b),将字符串b复制给从a开始的字符数组

// (3)strcpy(a,b),将字符串b复制给从a开始的字符数组 int main() { char s1[100], s2[100]; scanf("%s", s1); strcpy(s2, s1); cout << s2 << endl; return 0; }
2.3  遍历字符数组中的字符

遍历数组,当然采用for循环了,while循环也可以

//2.3 遍历字符数组中的字符 int main() { char a[100] = "hello world!"; for (int i = 0; i < strlen(a); i++) { cout << a[i] << endl; } return 0; }

这种方法行是行,但是每次都要计算strlen(a),花费了很多时间,有没有更省时的方法?

肯定有!把strlen(a)的结果存放在临时变量里面不就可以了~

int main() { char s1[100]; scanf("%s", s1); for (int i = 0, len = strlen(s1); i < len; i++) //将strlen(s1)的结果保存到len中 { cout << s1[i] << endl; } return 0; }

当然了,我们还可以在外面写:

int main() { char s1[100]; scanf("%s", s1); int len = strlen(s1); for (int i = 0; i < len; i++) { cout << s1[i] << endl; } return 0; }
练习1:只出现1次的字符

给定一个只包含小写字母的字符串,请你找出第一个仅出现一次的字符。如果没有,输出"no"。

思路:

①我们可以先定义str数组,用来读取输入的字符串,str数组长度可能会很大很大,所以定义为全局变量。

②再定义num数组,用来统计每个字母出现的次数,字母总数26个,因此,num数组长度为26。下标从0~25.,下标为0表示'a'出现的次数,下标为1表示'b'出现的次数....(相当于num数组是26个字母的映射)

③如何统计每个字母出现的次数呢?很简单,’a'的ASCII码值为97,对应num数组下标为0的位置,'a'出现的次数  = num数组下标为0位置的值;每当'a'出现1次,num[0]++;所以,表达式应为 num[str[i]-'a']

④当num数组出现任一下标的值为1的情况,即num[str[i]-'a'] == 1时,就是仅出现一次的字符。我们只需输出这个字符即可,即str[i],并且终止循环。

⑤如果num数组没有上面④这种情况,输出"no"

本道题代码如下:

//练习1:给定一个只包含小写字母的字符串, //请找出第一个仅出现1次的字符。 //如果没有,输出"no" int num[26] = { 0 }; char str[10010]; int main() { cin >> str; int len = strlen(str); for (int i = 0; i < len; i++) { num[str[i] - 'a']++; } for (int i = 0; i < len; i++) { if (num[str[i] - 'a'] == 1) { cout << str[i] << endl; return 0; //直接main函数结束,无需执行后续代码 } } cout<<"no"<<endl;//如果没找到,输出"no" return 0; }

第二个for循环也可以这样写:

for (int j = 0; j < 26; j++) { if (num[j] == 1) //如果num数组对应下标位置的值为1 { cout << char(j + 'a') << endl;//输出该字符,强转成char类型 return 0; } }
练习2:替换字符

把一个字符串中特定的字符全部用给定的字符替换,得到一个新的字符串

这道题,比较简单。代码如下:

//练习2:把一个字符串中特定的字符全部用给定的字符替换 //得到一个新的字符串 int main() { char str[31] = { 0 }; scanf("%s", str); //scanf函数的%s,会自动忽略空格,换行,Tab键 char c; scanf("\n%c", &c); //scanf函数的%c,任何数据都会被当作一个字符 //不管是数字还是空格、回车、Tab键它都会取回 for (int i = 0; str[i]; i++) { if (str[i] == c) { str[i] = '#'; } } puts(str); return 0; }

 也可以使用下面这种方法:

int main() { char str[31] = { 0 }; cin >> str; //用cin也可以 int len = strlen(str);//strlen函数的参数类型是char* char c; cin >> c; for (int i = 0; i < len; i++) { if (str[i] == c) { str[i] = '#'; } } cout << str << endl; //使用cout也可以 return 0; }
三、标准库类型  string

可变长的字符序列,比字符数组更加好用,需要引入头文件:#include<string>

3.1  定义和初始化 //3.1 定义和初始化 int main() { string s1; //默认初始化,s1是一个空字符串 string s2 = s1; //s2是s1的副本 string s3 = "hiya"; //s3是该字符串字面值的副本 string s4(10, 'c'); //s4的内容是 cccccccccc return 0; } 3.2  string上的操作

(1)string的读写

//3.2 string上的操作 //(1)string的读写 int main() { string s1, s2; cin >> s1 >> s2; cout << s1 << s2 << endl; return 0; }

注意:不能用printf直接输出string,需要写成:printf("%s",s.c_str());

(2)使用getline读取一整行

//(2)使用getline读取一整行 int main() { string s; getline(cin, s); cout << s << endl; return 0; }

(3)string 的 empty 和 size 操作(注意 size 是无符号整数,因此 s.size() <= -1 一定成立):

//(3)string的empty和size操作(注意:size是无符号整数,因此s.size()<=-1一定成立): int main() { string s1, s2 = "abc"; cout << s1.empty() << endl; // 1 cout << s2.empty() << endl; // 0 cout << s2.size() << endl; // 3 return 0; }

(4)string 的比较

支持 > < >= <= == != 等所有比较操作,按字典序进行比较

(5)为string对象赋值:

int main() { string s1(10, 'c'), s2; //s1 的内容是cccccccccc;s2是一个空字符串 s1 = s2; //赋值:用s2的副本替换s1的副本 //此时s1和s2都是空字符串 return 0; }

(6)两个string对象相加:

//2个string对象相加 int main() { string s1 = "abc", s2 = "def"; string s3 = s1 + s2; //abcdef s3 += s1 + s2; //abcdefabcdef cout << s3 << endl; return 0; }

(7)字面值和string对象相加:

做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来:

string s1 = "hello", s2 = "world"; string s3 = s1 + "," + s2 + '!'; cout << s3 << endl; //hello,world!

当把string对象和字符字面值及字符串字面值混再一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string:

string s4 = s1 + ","; //正确: 把一个string对象和有一个字面值相加 //string s5 = "hello" + ","; //错误: 两个运算对象都不是string, //本质上是2个const char*类型的指针相加 //2个都是字符串常量,肯定是不能改变,不能进行相加 string s6 = s1 + "," + "world"; //正确: 每个加法运算都有一个运算符是string //如果从左到右,有一个string类型的,后面才会隐式类型转换 //string s7 = "hello" + "," + s2; //错误: 不能把字面值直接相加,运算是从左到右进行的 //从左到右,先是"hello"+","相加,所以不会有隐式类型转换

 3.3  处理string对象中的字符

可以将string对象当成字符数组来处理

//3.3 处理string对象中的字符 //可以将string对象当成字符数组来处理 int main() { string s = "hello world"; for (int i = 0; i < s.size(); i++) { cout << s[i] << " "; } cout << endl; return 0; }

或者使用基于范围的for语句 

int main() { string s = "hello world"; //范围for //把字符串s里面的值依次赋给e for (auto e : s) { cout << e << " "; } cout << endl; return 0; }

 如果我们想改变字符串s里面的值,需要在范围for里面传递引用&

int main() { string s = "hello world"; for (char& c : s) { c = 'a'; } cout << s << endl; //aaaaaaaaaaa return 0; }
 练习3:信息加密

密码翻译,输入一个只包含小写字母的字符串,将其中的每个字母替换成它的后继字母,如果原字母是'z',则替换成'a'

emmm,咱们还是来画个图~

OK啦!本道题的代码如下:

//信息加密 int main() { string s; getline(cin, s); //如果用cin,遇到空格,会停止读取 //使用getline函数,读取一整行 int len = s.size(); //求字符串的长度 for (int i = 0; i < len; i++) { if (s[i] >= 'a' && s[i] <= 'z') { s[i] = 'a' + (s[i] - 'a' + 1) % 26; } else if (s[i] >= 'A' && s[i] <= 'Z') { s[i] = 'A' + (s[i] - 'A' + 1) % 26; } } cout << s << endl; return 0; }

我们还可以使用范围for来优化代码:

int main() { string s; getline(cin, s); //如果用cin,遇到空格,会停止读取 //使用getline函数,读取一整行 //这里要修改字符串s里面的值,必须传引用& for (auto& e : s) { if (e >= 'a' && e <= 'z') { e = 'a' + (e - 'a' + 1) % 26; } else if (e >= 'A' && e <= 'Z') { e = 'A' + (e - 'A' + 1) % 26; } } cout << s << endl; return 0; }
练习4:字符串长度

其实这道题,就相当于让我们模拟实现strlen函数。

代码如下:

//求字符串长度 int main() { char s1[100] = { 0 }; cin >> s1; int len = 0; for (int i = 0; s1[i] != '\0'; i++) { len++; } cout << len << endl; return 0; }

方法二:

int main() { string str; getline(cin, str); //使用getline函数读取一整行 cout << str.size() << endl;//调用size()函数 return 0; }
片尾

今天我们学习了字符串相关知识点,希望这篇文章对友友们有所帮助!!!

求点赞收藏加关注!!!

谢谢大家!!!

标签:

C++蓝桥杯基础篇(七)由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“C++蓝桥杯基础篇(七)