0%

Cpp基础(4)字符数组与字符串

字符数组与字符串

定义

字符型

用于表示单个字符 只占用1个字节,将对应的ASCII码放入存储单元
语法:char ch = 'a'
需要注意:
1.要用单引号将字符括起来
2.单引号内只能有一个字符,不可以是字符串

字符串型

用于表示一串字符
两种风格:

  1. C风格字符串: char 变量名[] = “字符串值” 要用双引号
  2. C++风格字符串: string 变量名 = “字符串值” 需要加入头文件 #include<string>

转义字符

用于表示一些不能显示出来的ASCII字符
常用的转义字符:/n 换行,将当前位置移到下一行开头
/t 水平制表,跳到下一个TAB位置,/t和其前面的内容一共占8个字符
// 代表一个反斜杠字符

QQ图片20200127110928

初始化与赋值

只可以在数组定义并初始化的时候才可以使用字符串字面值对字符数组初始化,一定要注意字符串字面值的结尾处还有一个空字符。不能用赋值语句将一个字符串常量或字符数组直接赋给另一个数组。

1
2
3
4
5
6
7
8
9
10
11
char a1[] = {'C', '+', '+'};          //列表初始化,没有空字符
char a2[] = {'C', '+', '+', '\0'}; //列表初始化,含有显式的空字符
char a3[] = "C++"; //用字符换字面值初始化,自动添加表示字符串结束的空字符
const char a4[6] = "Daniel" //错误:没有空间放空字符

str1[] = "China"; //错误
str1 = "China"; //错误
str2 = str1; //错误

//利用二维数组存储多个字符串
char weekday[7][11] = {"Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday","Ssturday"};

字符/字符数组/字符串的输入与输出

一个字符的输入

直接用cin输入字符。

cin这一输入操作,遇到结束符(Space, Tab, Enter)就会结束,且对于结束符,并不保存到变量中,但最后一个Enter会在缓冲区。

1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;
int main(){
char c;
cout<<"enter a sentence"<<endl;
while(cin>>c) //abc def g
cout<<c; //abcdefg
return 0;
}

cin.get()函数输入

可以用于读入一个字符;有2中形式:

无参数cin.get(),可用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补三参数的cin.get(ch, 10, '/n')的不足。

1
2
3
4
char c;
cout<<"enter a sentence"<<endl;
while( (c=cin.get())!= EOF ) //abc def g
cout << c; //abc def g

一个参数cin.get(char)

1
2
3
4
5
char c;
cout<<"enter a sentence"<<endl;
//读取一个字符赋给字符变量c
while( cin.get(c) ) //abc def g
cout << c; //abc def g

要注意的是,cin.get()遇到结束符停止读取,但并不会将结束符从缓冲区丢弃。

1
2
3
4
5
6
char ch1,ch2;  
cout<<"请输入两个字符:"<<endl;
cin.get(ch1);//或ch1 = cin.get();
cin.get(ch2);
cout<<ch1<<" "<<ch2<<endl;
cout<<(int)ch1<<" "<<(int)ch2<<endl;

输入a[Enter],读取到结束符'/n',其仍在缓冲区中被存入ch2,在输出a之后,第二次输出'/n'即换行,而输出的第二个ASCII码值为10,即'/n'的ASCII值,说明cin.get()遇到结束符并不会将之删除。

QQ图片20200127172134

getchar()输入字符

1
2
3
4
char c;
cout<<"enter a sentence"<<endl;
while( c = getchar() ) //abc def g 不跳过任何字符
cout << c; //abc def g

字符串的输入

直接用cin输入字符

1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;
int main() {
char str[10];
cout << "enter a sentence" << endl;
while (cin >> str)
cout << str << endl;
return 0;
}

QQ图片20200127120550

cin.get()函数输入

有三个参数的cin.get()函数:cin.get(ch, 10, '/n')

读取10-1(10减1=9,最后一个为'/0')个字符(包括空格),赋给指定的字符数组,;如果在读取9个字符之前,遇到指定的终止字符'/n',则提前结束读取(如果第3个参数没有指定,则默认为'/n'),而结束符仍在缓冲区中;读取成功返回非0值(真),如失败(遇到文件结束符)则返回0值(假)。

还要一点要注意,cin.get(ch, 10, '/n'),当第一个输入字符为结束符时,缓冲区将无该结束符。

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;
int main() {
char ch1[20],ch2[20];
cout << "enter a sentence" << endl;
cin.get(ch1,10,'o'); //指定终止符为'o'
cin.get(ch2,10);
cout << ch1 << endl;
cout << ch2 << endl;
return 0;
}

QQ图片20200127173835

输入:we are good friends[Enter],由于遇到结束符 'o' ,首先读入we are g到ch1,此时ood friends仍在缓冲区,当执行cin.get(ch2,10)会直接从缓冲区读入ood frien(只能读入9个字符),而不需要申请从键盘输入。

cin.getline()函数输入

用法与上面的cin.get()类似,但也有区别:

cin.get()当输入的字符串在结束符之前的长度超过接收长度时,不会引起cin函数的错误,剩余的字符会留在缓冲区,后面若有cin操作,会继续从缓冲区读取;当cin.getline()输入超长时,会引起cin函数的错误,后面的cin操作将不再执行。

cin.get()每次读取一整行并把由Enter键生成的换行符'/n'留在输入队列中,然而cin.getline()每次读取一整行并把由Enter键生成的换行符抛弃。

cin.get()遇到结束符是停止读取,缓冲区指针不移动;cin.getline()遇到结束符时,缓冲区指针移到终止标志字符之后。

1
2
3
4
5
6
char ch1[20],ch2[20];
cout << "enter a sentence" << endl;
cin.getline(ch1,10,'o'); //指定终止符为'o'
cin.getline(ch2,10);
cout << ch1 << endl;
cout << ch2 << endl; //注意与上节的区别

QQ图片20200127191238

一个需要注意的地方:cin这一输入操作,遇到结束符(Space, Tab, Enter)就会结束,且对于结束符,并不保存到变量中,但最后一个Enter会在缓冲区。而无参数cin.get(),可用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补三参数的cin.get(ch, 10, '/n')的不足。

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
int main() {
char a[10][10];
int n = 0;
cin >> n; //输入7[Enter],7存入n,[Enter]仍在缓冲区,
for (int i = 0; i < n; i++)
cin.getline(a[i], 10); //当执行cin.getline(a[1],10)时,[Enter]被读取到a[1]
for (int i = 0; i < n; i++)
cout << a[i] << endl;
return 0;
}

QQ图片20200127192425

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
int main() {
char a[10][10];
int n = 0;
cin >> n; //输入7[Enter],7存入n,[Enter]仍在缓冲区
cin.get(); //用cin.get来舍弃缓冲区的[Enter],就不会出现上面的情况了
for (int i = 0; i < n; i++)
cin.getline(a[i], 10);
for (int i = 0; i < n; i++)
cout << a[i] << endl;
return 0;
}

QQ图片20200127193235

字符串的输出

cout输出字符数组

1
2
3
4
5
6
7
8
#include<iostream>
using namespace std;
int main()
{
char a[10] = "Computer";
cout << a;
return 0;
}

QQ图片20200127220421

需要注意的是,cout输出有终止条件(一般是碰到'\0'),当用字面值初始化字符数组时,会自动在串尾加上'\0';当用列表初始化字符数组时,若不在串尾加上’\0’,用cout输出时就不知道何时停止,可能读到内存里其他随机的位置,显示就会在字符串后面出现乱码。

1
2
char a[8] = { 'C','o','m','p','u','t','e','r' };
cout << a;

QQ图片20200127220426

例子

1.字符串加密:输入一个字符串,把每个字符变成它后续字符,如果是’Z’或者’z’,则对应变成’A’或者’a’,空格则不变。然后将变换后的字符串输出;要求能够接受连续输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
int main()
{
char str[200];
while (cin.getline(str, 200))
{
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] == 'Z'){
str[i] = 'A'; continue;
}
if (str[i] == 'z'){
str[i] == 'a'; continue;
}
if (str[i] == ' ')
continue;
str[i]++;
}
cout << str << endl;
}
return 0;
}

QQ图片20200127203829

2.字符串连接:输入两个字符串,将其中较短的串接到较长的串的后面。不使用系统函数strcat,每个输入的串的长度不超过20。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
using namespace std;
int main() {
int len1, len2;
char str1[40], str2[40];
cin.getline(str1,20);
cin.getline(str2, 20);
for (len1 = 0; str1[len1] != '\0'; len1++);
for (len2 = 0; str2[len2] != '\0'; len2++);
if (len1 >= len2)
{
for (len2 = 0; str2[len2] != '\0'; len2++)
str1[len1++] = str2[len2]; //后置++,先执行表达式,再len+1
str1[len1] = '\0'; //必须要加,要不然cout<<str1会出错
}
else
{
for (len1 = 0; str1[len1] != '\0'; len1++)
str2[len2++] = str1[len1];
str2[len2] = '\0';
}
cout << str1 << endl;
cout << str2 << endl;
return 0;
}

QQ图片20200127213528

3.统计单词数:输入一个英文句子(不超过80个字母),统计其中有多少个单词,单词之间用空格分开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
using namespace std;
int main() {
char str[80];
int num = 0, flag = 0;
cin.getline(str, 80);
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] == ' ')
flag = 0;
else if (flag == 0)
{
flag = 1; num++;
}
}
cout << "字符串中有" << num << "个单词" << endl;
return 0;
}

QQ图片20200127214732

当输入不是很严格时,比如有数字,或输入不规范,比如标号后不加空格等等,上面的程序会多算单词数。可以改成如下的程序,直接判断是不是字母。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
using namespace std;
int main() {
char str[80];
int num = 0, flag = 0;
cin.getline(str, 80);
for (int i = 0; str[i] != '\0'; i++)
{
if ((str[i] >= 'A'&& str[i] <= 'Z') || (str[i] >= 'a'&&str[i] <= 'z'))
flag = 0;
else if (flag == 0)
{
flag = 1; num++;
}
}
cout << "字符串中有" << num << "个单词" << endl;
return 0;
}

QQ图片20200127215300

对于char数组char s[100],要读入一整行可以用cin.getline(s, 100);,或者fgets(s, 100, stdin);

要输出可以用printf("%s\n", s);,等价于puts(s);

char数组常用的函数有(要引入头文件#include<string.h>):

  • strlen(str),求字符串的长度('\0'不计入其中);
  • strcmp(a, b),比较两个字符串的大小,a < b 返回-1,a == b 返回0,a > b返回1。这里的比较方式是字典序(字典序一般和贪心相关);
  • strcpy(a, b),将字符串b复制给从a开始的字符数组。

C++标准库类型string

C++标准库类型string字符串是可变长的字符序列,比字符数组更加好用。需要引入头文件#include<string>,读入string字符串可以用getline(cin, s);读入一整行;使用cin >> s;读入,遇到结束符(Space, Tab, Enter)就会终止。

定义和初始化

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<string>
using namespace std;

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

string的读写:(不能用printf直接输出string,需要写成:printf(“%s”, s.c_str());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<string>
using namespace std;

int main()
{
string s1, s2;
cin >> s1;
getline(cin, s2); //读入一行
cout << s1 << " " << s2 << endl;
//printf("%s", s1.c_str());
//puts(s1.c_str());
return 0;
}

常用函数 / 操作:

  • .empty()用来判断字符串是否为空,返回一个布尔值;

  • .size()操作返回字符串的长度,其时间复杂度为$O(1)$(注意size是无符号整数,因此 s.size() <= -1一定成立);

  • string的比较:直接使用关系符<, <=, >, >=, ==, !=,按字典顺序比较;
1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<string>
using namespace std;

int main()
{
string s1, s2 = 'abc';
cout << s1.empty() << endl; //1
cout << s2.empty() << endl; //0
cout << s2.size() << endl; //3
return 0;
}
  • string对象赋值:s1 = s2;,用s2的副本替换s1的副本;
  • string对象赋值:
1
2
3
string s1(10, ‘c’), s2;		// s1的内容是 cccccccccc;s2是一个空字符串
s1 = s2; // 赋值:用s2的副本替换s1的副本
// 此时s1和s2都是空字符串
  • 两个string对象相加:
1
2
3
string s1 = “hello,  ”, s2 = “world\n”;
string s3 = s1 + s2; // s3的内容是 hello, world\n
s1 += s2; // s1 = s1 + s2
  • 字面值和string对象相加:做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来;当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
1
2
3
4
5
6
7
8
string s1 = “hello”, s2 = “world”;		// 在s1和s2中都没有标点符号
string s3 = s1 + “, “ + s2 + ‘\n’; // s3的内容是 hello, world\n

string s4 = s1 + “, “; // 正确:把一个string对象和有一个字面值相加
string s5 = “hello” +”, “; // 错误:两个运算对象都不是string

string s6 = s1 + “, “ + “world”; // 正确,每个加法运算都有一个运算符是string
string s7 = “hello” + “, “ + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的

处理string对象中的字符

  • 可以将string对象当成字符数组来处理:
1
2
string s = "hello world";
for(int i = 0; i < s.size(); i++) cout << s[i] << endl;
  • 或者使用基于范围的for语句:
1
2
3
string s = "hello world";
for(char c : s) cout << c << endl;
for(char &c : s) c = 'a'; //改变s中的每一个字符