蓝桥备赛(四)- 数组(下)

一 、 字符数组

1.1 介绍

数组的元素如果是字符类型 , 这种数组就是字符数组 , 字符数组可以是一维数组 , 可以是二维数组 (多维数组)。

接下来主要讨论一维的字符数组 :

char arr1[5] //一维数组

char arr2[3][5] // 二维数组

C语言 中使用 双引号 括起来一串字符表示字符串 , 这种方式虽然在C++中也是支持的(C++提供了string) ,但我们一般会将这种字符串称为 C语言风格的字符串 。如果需要将一个C语言风格的字符串存储起来 , 就可以使用字符数组

1.2 初始化

1.字符数组的创建: char a[10];

2 . 字符数组的创建同一维数组的创建一样 , 就不再赘述 , 字符串数的初始化有2种方式:

#include <iostream>
#include <cstdio>
using namespce std;

int main()
{
	//1.直接使用字符串初始化
	char ch1[10] = "abcdef";//后面的默认为0
	char ch2[] = "abcdef";//数组大小根据初始化进行调整
	
	//2.用字符进行初始化
	char ch3[10] = {'a','b','c','d','e','f'};//后面默认为0
	char ch4[] =  {'a','b','c','d','e','f'};
	 
	return 0;
}

如果调试看⼀下 ch2 和 ch4 数组的内容,我们会明显的发现,数组 ch2 中多⼀个 '\0' 字符,这是因为字符串的末尾其实隐藏⼀个 '\0' 字符,这个 '\0' 是字符串的结束标志,在打印字符串的时候遇到 '\0' ,打印结束。

注意 ,字符数组的打印和整型数组的打印一定要区分开来 !!!

1.3 字符串长度 - strlen

问题引入:我们看下面的ch1 , 数组长度是10 , 但是数组并没有被字符填满 , 字符串的长度是6,这时候要怎么计算 ?

C/C++中有一个库函数 : strlen , 可以求字符串长度 , 其实统计的就是字符串中 \0 之前的字符个数 。strlen 需要的头文件是<cstring>

strlen - C++ Reference

size_t strlen ( const char * str );
//str - 指针,存放的是字符串的起始地址,从这个地址开始计算字符串的长度

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch1[10] = "abcdef";
	int n = strlen(ch1); 
	cout << n << endl;
	int sz = sizeof(ch1)/sizeof(ch1[0]);
	cout << sz << endl;
	return 0;
}

字符数组的元素个数 , 大小是用sizeof求 ; 字符数组的长度是用strlen来求!

1.4 字符数组的输入

输入没有空格字符串

1)使用scanf函数和字符数组来实现:(这里推荐使用VS,调试观察)

数组首元素就是数组的地址

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	scanf("%s", ch);
	//输出
	printf("%s", ch);
	return 0;
}

我们发现会报错 , 不用担心 , 按照下面添加语句即可 :

#define _CRT_SECURE_NO_WARNINGS 1

继续运行 :

按下F10 进行调试 , 可以看到没有初始化的字符元素默认为'\0'

2)使用cin 和 字符数组来实现:

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	cin >> ch;
	//输出
	cout << ch << endl;
	return 0;
}

3) 上面两个代码是将字符串读取后从数组的其实位置开始存放的 , 当然也可以指定位置存放 。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	//+2的意思就是跳过两个元素 , 就是第三个元素
	cin >> ch + 2;
	//输出
	cout << ch + 2<< endl;
	return 0;
}

那么从第n个元素开始存放 , 就应该是 cin >> arr + n ; 使用scanf 也是一样的 。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	scanf("%s", ch + 2);
	//输出
	printf("%s", ch + 2);
	return 0;
}

输入有空格的字符串

1)发现问题:

前面我们讲解了scanf 和 cin 读取不含空格的字符串 , 一切正常 , 那如果我们输入的字符串中带有空格 , 实际上是无法正常打印出我们想要的结果 , 而是读到空白字符 , 编译器默认读完

2)scanf的方式 :

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	scanf("%s", ch );
	//输出
	printf("%s", ch );
	return 0;
}

当输入"abc def"的时候 , 实际上scanf 只读取了abc就结束了 , 也就是相当于遇到空格就结束了。


这里特别说一下占位符 %s 。 **它其实不能简单地等同于字符串。**它的规则是,从当前第⼀个
非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止

%s 的读取不会包含空白字符,所以无法用来读取多个单词,除非多个 %s ⼀起使用。
这也意味着, scanf() 不适合读取可能包含空格的字符串,比如书名或歌曲名。 另外有⼀
个细节注意⼀下, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个 \0 字符

同时scanf() , 将字符串读入字符数组的时候 , 不会检测字符串是否超过了数组的长度 。

为了防止这种情况 , 使用%s 占位符的时候 , 可以指定读入字符串的最长长度 , 即写成**%[m]s , 其中的[m]是一个整数 , 表示读取字符串的最长长度 , 后面的字符将被丢弃**。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char ch[20] = { 0 };
	//输入
	scanf("%5s", ch);
	//输出
	printf("%s", ch);
	return 0;
}

上面的%5s表示最多读取用户输入的10个字符 ,后面的字符将被丢弃 , 这样就不会有数组溢出的风险了。

3)cin的方式:

结果都一样~~~~, 没有任何区别!!!

其实cin在读取一个字符串的时候 ,在遇到空白字符的时候 , 就认为字符串就结束了往后读取剩余的字符,同时将已经读取到的字符串末尾加上\0 , 直接存储起来 。

4)解决问题:

4-1)gets 和 fgets

使用gets函数的方式 , 这种方式能解决问题 , 但是 因为gets 存在 安全性问题 , 在C++11中取消了gets , 随后给出了更加安全的方案:fgets

char * gets ( char * str );
char * fgets ( char * str, int num, FILE * stream );
| | |
地址 最多存放的大小 流

gets 原理 : gets 是从第一个字符开始读取的 , 一致读取到\n停止 , 但是不会读取\n , 也就是读取到的内容中不包含\n , 但是会在读取到的内容后面自动加上 \0 。 (读到\n,但不读取\n,会自动加\0)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[10] = {0};
	gets(arr);
	printf("%s",arr);
	return 0;
}

小提示 : Dec - C++中使用gets函数 , 没有报错 , 但是在其他IDE上 , 比如VS , 就会报错。所以慎用gets !!!!

4-2)fgets

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[10] = {0};
	fgets(arr,sizeof(arr),stdin);
	printf("%s",arr);
	return 0;
}

fgets 原理 : fgets 也是从第一个字符开始读取,最多读取 num-1 个字符,最后⼀个位置留给 \0 ,如果 num 的长度是远大于输入的字符串长度,就会⼀直读取到 \n 停止,并且会读取 \n ,将 \n 作 为读取到内容的⼀部分,同时在读取到的内容后自动加上 \0 。(最多读num-1个,读取\n ,然后加\0) --> 这时候需要注意遍历数组的时候,需要考虑换行问题。

用gets 和 fgets 同时输入"abc def" , 两者差异如下:

4-3) scanf

当然C语言中使用 scanf 函数其实也能做到读取带有空格的字符串,只是不常见。
将 "%s" 改成 "%[^\n]s" , 其中在 % 和 s 之间加上了 [^\n] ,意思是⼀直读取,直到遇到
\n ,这样即使遇到空格也就不会结束了。 这种方式读取,不会将 \n 读取进来,但是在读取到的字符串末尾加上 \0 。(不读取\n , 自动加\0)

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[10] = { 0 };
	scanf("%[^\n]s", arr);
	printf("%s", arr);
	return 0;
}

4-4)getchar

使用getchar 逐个字符的读取 , 也是可以读取一个字符串的。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[20];
	int ch = 0;
	int i = 0;
	while ((ch = getchar()) != '\n')
	{
		arr[i++] = ch;
	}
	printf("%s\n", arr);
	return 0;
}

1.5 字符数组的输出

1 . C语言中可以在printf 函数中使用 %s 占位符的方式 , 打印字符数组中的字符串。

2 . C++中使用 cout , 可以直接打印字符数组中的字符串内容 。

3 . 也可以采用循环的方式逐个字符打印字符串内容 。

---> 字符串的结束标志是\0

----> strlen

4 . 如果没有\0的字符数组 , 计算数组的长度 , for循环

方法一:printf 、cout

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[] = "Hello Wrold!";
	printf("%s\n",arr);
	cout << arr << endl;
	return 0;
}

方法二:循环

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[] = "Hello Wrold!";
	int i = 0;
	while(arr[i] != '\0')
	{
		cout << arr[i];
		i++;	
	} 
	cout << endl;
	return 0;
}

方法三:单个字符打印,根据字符长度来逐个打印 (strlen 可以用来计算字符串的长度,不包含\0,记住需要包含头文件<cstring>

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[] = "Hello Wrold!";
	for(int i = 0; i< strlen(arr);i++)
	{
		cout << arr[i];	
	}	
	cout << endl;
	return 0;
}

方法四:当我们遇到没有\0的字符数组的时候 , 我们可以计算数组的长度来打印字符数组

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr[] = {'a','b','c','d'};
	int sz = sizeof(arr)/sizeof(arr[0]);
	for(int i = 0; i< sz;i++)
	{
		cout << arr[i];	
	}	
	cout << endl;
	return 0;
}

方法很多 , 根据需求来选择!

1.6 strcpy

使用字符数组可以存放字符串 , 但是字符数组能否直接赋值呢?

比如:
char arr1[] = "abcdef";
char arr2[20] = {0};
**arr2 = arr1;//这样赋值可以吗?
不可以 , 常量不可改
其实C/C++中有一个库函数 --> strcpy , 可以拷贝字符串

strcpy - C++ Reference

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = {0};
	strcpy(arr2,arr1);
	cout << arr2 << endl;
	return 0;
}

1.7 strcat

在一个字符的末尾追加一个字符串 , 那字符串能直接追加吗?
char arr1[20] = "hello ";
char arr2[] = "world";
**arr1 += arr2;//这样也是不行的
其实C/C++中有一个库函数strcat , 可以实现该功能。
char * strcat ( char * destination, const char * source );

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	char arr1[20] = "Hello ";
	char arr2[] = "World!";
	strcat(arr1,arr2);
	cout << arr1 << endl;
	return 0;
}

注意:追加到的数组需要空间大小足够,否组会越界

1.8 练习

练习一:自动修正

P5733 【深基6.例1】自动修正 - 洛谷

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 110;
char arr[N];
int main()
{
	cin >> arr;
	//cin输入字符串后,会在末尾存放\0 
	//调整字符串
	int i = 0;
	while(arr[i] != '\0')
	{
		if(arr[i] >= 'a' && arr[i] <= 'z')
		{
			arr[i] -= 32;
			//小写转大写
		}
		i++; 
	} 
	cout << arr << endl;
	return 0;
}

除了借助'\0' , 还可以借助strlen 计算字符数组的长度 :

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 110;
char arr[N];
int main()
{
	cin >> arr;
	int len = strlen(arr);
	int i = 0;
	for(int i = 0;i < len ; i++)
	{
		if(arr[i] >= 'a' && arr[i] <= 'z')
		{
			arr[i] -= 32;
			//小写转大写
		}	
	}
	cout << arr << endl;
	return 0;
}

这里再给大家介绍两组函数:islowe 和 tolower , 需要的头文件是<cctype>

islower - C++ Reference

tolower - C++ Reference

字符分类函数和字符转换函数:<cctype> (ctype.h) - C++ Reference

**int islower ( int c ); //判断字符是否是小写字母
**int tolower ( int c ); //转换成小写字母

1 . islower 是C/C++中提供的一个判断字符是否是小写字符的一个函数 ,如果c是小写字母 , 函数返回一个非0的数字 , 如果不是小写字母 , 函数返回 0, 还有一个函数是 issupper ,是判断是否大写字母的 。

2 . tolower 是C/C++中提供的一个将参数C 从大写字母转化成小写字母的函数 ,如果C是小写字母将什么也不发生。

有了以上的函数,我们可以对这道题目的代码进行修改。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
using namespace std;

const int N = 110;
char arr[N];
int main()
{
	cin >> arr;
	for(int i = 0;arr[i] != '\0';i++)
	{
		if(islower(arr[i]))
			arr[i] = toupper(arr[i]);
	}
	cout << arr << endl;
	return 0;
}

练习二:统计数字字符个数

B2109 统计数字字符个数 - 洛谷

#include <iostream>
#include <cstdio>
#include <cstring> 
using namespace std;

const int N = 265;
char arr[N];
int main()
{
	//使用fgets读取带空格的字符串时候,会读取\n,并将\n也读取到arr中,然后末尾加\0 
	fgets(arr,265,stdin);
	int i = 0;
	int c = 0;
	while(arr[i] != '\n')
	{
		if(arr[i] >= '0' && arr[i] <= '9')
			c++;
		i++;	
	}
	cout << c << endl;
	return 0;
}

判断一个字符是否是数字字符, 有一个函数是isdigit , 可以直接使用
int isdigit ( int c );
如果参数c是数字字符 , 则返回非0的数值 , 如果不是数字字符 , 则返回0
需要包含头文件<cctype>

#include <iostream>
#include <cstdio>
#include <cctype> 
using namespace std;

const int N = 265;
char arr[N];
int main()
{
	//使用fgets读取带空格的字符串时候,会读取\n,并将\n也读取到arr中,然后末尾加\0 
	fgets(arr,265,stdin);
	int i = 0;
	int c = 0;
	while(arr[i] != '\n')
	{
		if(isdigit(arr[i]))
			c++;
		i++;	
	}
	cout << c << endl;
	return 0;
}

练习三:整理药名

信息学奥赛一本通(C++版)在线评测系统

#include <iostream>
#include <cstdio>
#include <cctype> 
using namespace std;

const int N = 25;
char arr[N];
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		//输入药名
		cin >> arr;
		//处理第一个字符 
		if(islower(arr[0])) 
			arr[0] = toupper(arr[0]);
		int j = 1;
		while(arr[j] != '\0')
		{
			if(isupper(arr[j]))
				arr[j] = tolower(arr[j]);
			j++;
		}
	//输出
	cout << arr << endl; 
	} 
	return 0;
}

练习四:基因相关性

B2111 基因相关性 - 洛谷

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 510;
char a1[N];
char a2[N];
int main()
{
	double n = 0;
	cin >> n;
	cin >> a1;
	cin >> a2;
	int len = strlen(a1);
	int c = 0;
	for(int i = 0; i < len ; i++)
	{
		if(a1[i] == a2[i])
			c++;
	}
	if(c*1.0 / len >= n)
		cout << "yes" << endl;
	else
		cout << "no" << endl; 
	return 0;
}

练习五:输出亲朋字符串

B2113 输出亲朋字符串 - 洛谷

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 110;
char arr[N];
int main()
{
	cin >> arr;
	
	//n-1
	int i = 0;
	while(arr[i+1] != '\0')
	{
		char tmp = arr[i] + arr[i+1];
		cout << tmp;
		i++;
	}
	//n
	cout << (char)(arr[i] + arr[0]) << endl;
	return 0;
}

方法二 : 我们发现 (最后一个元素的下标) % 元素个数 == 第一个元素的下标 , 借助这一个特点 , 我们可以统一用一个式子来求亲朋数 , 需要注意的是 , % 运算符 的优先级比 + 高 , 所以需要加上小括号

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 110;
char arr[N];
int main()
{
	cin >> arr;
	int i = 0;
	int len = strlen(arr);
	while(arr[i] != '\0')
	{
		//% 的优先级比 + 的高 
		char tmp = arr[i] + arr[(i+1)%len];
		cout << tmp;
		i++;
	}
	return 0;
}

练习六:验证子串

B2118 验证子串 - 洛谷

介绍一个函数 , 可以用来判断字串 : strstr

strstr - C++ Reference
const char * strstr ( const char * str1, const char * str2 ) ;
这个函数本质上是用来查找子字符串的 。在str1 字符串中查找str2 字符串第一次出现的位置,如果找到了,就返回第一次出现的地址 ; 如果没有找到 , 就返回NULL(0)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 25;
char a1[N];
char a2[N];
int main()
{
	cin >> a1;
	cin >> a2;
	if(strstr(a1,a2))
		cout << a2 << " is substring of " << a1 << endl;
	else if(strstr(a2,a1))
		cout << a1 << " is substring of " << a2 << endl;	
	else
		cout << "No substring" << endl;
	return 0;
 } 

练习七:找到第一个只出现一次的字符

B2110 找第一个只出现一次的字符 - 洛谷

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

//方法一:暴力求解
const int N = 1110;
char arr[N];
int main()
{
	cin >> arr;
	int i = 0;
	int flag = 0;//表示没有出现仅一个的字符 
	while(arr[i])
	{
		int j = 0;
		int c = 0;
		while(arr[j])
		{
			if(arr[i] == arr[j])
			{
				c++;
			}
			j++;
		}
		if(c == 1)
		{
			flag = 1;
			cout << arr[i] << endl;
			break;
		}
		i++;
	}
	if(flag == 0)
		cout << "no" << endl;
	return 0;	
} 

方法二:题目说字符串中只有小写字母,**小写字母的ASCII值的范围是:97~122,在C和C++中每个字符都有ASCII值,**标准的ASCII码表中有128个字符,ASCII值的范围是0~127.
所以我们创建⼀个128元素的整型数组 ,下标分别是0~127,下标正好和字符的ASCII值范围对应,那么整型的数组的⼀个元素就为⼀个字符计数就可以。每读取⼀个字符,根据字符的ASCII值将数组中下标为字符ASCII值的元素值+1,相当于统计这个字符出现的次数。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1110;
char arr[N];
int num[128] = {0};
int main()
{
	int i = 0;
	while((arr[i] = getchar())!='\n')
	{
		num[arr[i]]++;
		i++;
	}
	i = 0;
	int flag = 0;
	while(arr[i])
	{
		if(num[arr[i]] == 1)
		{
			flag = 1;
			cout << arr[i] << endl;
			break;
		}
		i++;
	}
	if(flag == 0)
		cout << "no" << endl; 
	return 0;
}
相关推荐
Awkwardx几秒前
C++初阶—list类
开发语言·c++
Hello.Reader4 分钟前
理解 Rust 的并发特性:`Send` 和 `Sync` 特征
开发语言·后端·rust
2501_902556237 分钟前
C++ 中 cin 和 cout 教程
数据结构·c++
心灵Haven8 分钟前
1_安装JDK和Hadoop
java·开发语言·hadoop
Swift社区44 分钟前
【Swift 算法实战】利用 KMP 算法高效求解最短回文串
vue.js·算法·leetcode
萌の鱼1 小时前
leetcode 73. 矩阵置零
数据结构·c++·算法·leetcode·矩阵
好看资源平台1 小时前
‌KNN算法优化实战分享——基于空间数据结构的工业级实战指南
数据结构·算法
Jumbo星1 小时前
ms-swift 3.x和2.x中参数不一致的暗坑
开发语言·ios·swift
AllYoung_3621 小时前
WebUI 部署 Ollama 可视化对话界面
人工智能·深度学习·算法·语言模型·aigc·llama
B.-1 小时前
在已有的原生 App 里嵌入 Flutter 页面的方法
开发语言·flutter·macos·cocoa