【小课堂】六、字符串

我们经常需要使用计算机处理大量的字符,因而C/C++引入字符串(string)以支持对大量字符操作。
其中,C++语言提供了C风格字符串C++风格字符串(STL)两种选择:C风格的字符串操作与C语言完全一致,而C++风格的字符串则以面向对象的范式支持了更多操作。

字符串常量

C/C++中的字符串常量就是以英文双引号括起来的一段字符:

1
"hello,world"

像scanf,printf函数,传的参数也就是字符串常量。

C风格的字符串

在C语言中,字符串就是一种特殊的字符型数组,唯一与普通字符型数组不同的是,字符串以一个特殊字符’\0’(ASCII码也为0)结尾。

1
2
3
4
char a[100]={'a','b','c'};//这是一个字符型数组

char b[100]={'a','b','c','\0'};//这是一个C风格字符串

由于数组初始化时会将未赋值的空间赋为0,因此字符数组a也可以看做一个字符串,而b[3]==’\0’就是一个字符串。但为了区分,我们有必要在字符串结尾加一个’\0’做标志。

C风格输入输出

引入字符串概念后,就可以对字符进行整体的输入输出操作了。

1
2
3
char s[100];
scanf("%s",s);
printf("%s",s);

输入:
abc
输出:
abc
使用scanf将字符串”abc”整体读入字符串s,并使用printf进行整体的输出。

这里有个重要的细节:用scanf输入变量时,需要使用取地址运算符&进行操作,而输入字符串时则不加,这一差异需要用心记忆。(原因则较为复杂不必深究,与数组类型的本质有关。数组名实际上是指向首元素的一个特殊指针,因而字符串传入时相当于直接传入了地址。)

C风格字符串的操作

另外,C语言还提供了更多现成的字符串操作(即定义了许多现成的字符串函数),都在头文件string.h中;如果使用C++,头文件则是cstring,里面的内容是完全一样的。这里只是简单介绍常用的一些操作,更多操作如有需要可以自行查询。

gets、puts

与scanf,printf类似,是字符串的输入输出函数。只是scanf读取字符串和其他数据一样,遇到空白符号即停止(空格、回车),而gets允许读取代空格的字符串:

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

char a[100];

int main(){
scanf("%s",a);
printf("%s",a);

return 0;
}

输入:
abc def
输出:
abc

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

char a[100];

int main(){
gets(a);
puts(a);
return 0;
}

输入:
abc def
输出:
abc def

strcpy

和数组一样,C风格的字符串不允许直接赋值为一个字符串常量:

1
2
char s[10];
//禁止: s="hello";

要赋值一个字符串,需要使用函数strcpy(string copy):

1
2
3
4
5
6
7
char s1[10];
strcpy(s1,"hello");
puts(s1);

char s2[10];
strcpy(s2,s1);
puts(s2);

输出:
hello
hello

strcmp

用于比较两个字符串的大小(也就是字典序,就是英文字典中单词的排序方式)。
strcmp(s1,s2):如果s1>s2返回正整数,s1=s2返回0,s1<s2返回负整数:

1
2
3
4
5
char s1[20],s2[20];
strcpy(s1,"apple");
strcpy(s2,"application");
if(strcmp(s1,s2)>0) puts(s1);
else puts(s2);

输出:
application

strlen

返回一个字符串的长度(字符数,’\0’不计在内):

1
2
3
4
5
6
printf("%d\n",strlen("apple"));

char s[20];
strcpy(s,"ball");
printf("%d\n",strlen(s));

输出:
5
4

字符串的一般操作

和数组一样,我们也可以使用下标访问、操作字符串的每一个字符:

1
2
3
4
5
6
7
char s[20];
scanf("%s",s);
int length=strlen(s);
for(int i=0;i<length;i++)
s[i]++;
printf("%s",s);

输入:
abcde
输出:
bcdef

我们发现,字符串中的每一个字符,都通过访问下标的方式加一,最后整体输出。

其它字符串函数

以上列出的是最常用的一些字符串函数,而string.h(C++中cstring)还提供更多字符串函数供使用。这里不作要求,只是简单列出,如在编写过程中有需要可以自行查询。

  • strcat(s1,s2):将参数s2字符串拷贝到参数s1字符串尾
  • strlwr(s):将字符串中的大写字母转化为小写
  • strupr(s):将字符串中的小写字母转化为大写
  • strchr(s,c):在一个串中查找给定字符的第一个匹配之处
  • strrev(s):将一个字符串反向

C++风格的字符串

C++除了支持使用C风格字符串和所有的C字符串函数外,还提供另一种“C++风格的字符串”。(与C风格的最大区别是,C++字符串是建立在面向对象的基础上的一个字符串类,属于C++的标准模板库STL),除了提供更多操作和封装带来更好的稳定性外,字符串作为一个类型还可以与STL中的其他容器协同工作)

string类型

在学习面向对象编程的概念之前,我们只需知道,C++风格的字符串是C++内置的一种类型:string类型
要使用string类型,我们首先要使用头文件string(不是C语言的string.h,也不是C++的cstring)

1
2
3
#include<string>
using namespace std;
string s1,s2="hello";

这样,就简单完成了两个字符串s1,s2的定义,并给字符串s2赋了初值”hello”。我们发现:string类型是可以用字符串常量赋值的。

string操作

和C++的其他类型一样,string也可以使用cin/cout进行简单的输入输出:

1
2
3
string s1;
cin>>s1;
cout<<s1;

输入:
1234
输出:
1234

string之间、string和字符串常量可以进行赋值:

1
2
3
4
5
string s1,s2="hello";
s1=s2;
cout<<s1<<endl;
s1="abc";
cout<<s1<<endl;

输出:
hello
abc

string类型提供一些简单的运算,简化了字符串处理的流程:

1
2
3
运算符有:
+ += //连接
>= <= > < == != //比较字典序

如:

1
2
3
4
string s1="aaa",s2="bbb";
cout<<s1+s2<<endl;
s2+=s1;
cout<<s2<<endl;

输出:
aaabbb
bbbaaa

与C风格一样,string类型也可以通过下标访问每一个字符,下标从0开始

1
2
3
4
string s="abcde";
for(int i=0;i<5;i++)
s[i]++;
cout<<s;

输出:
bcdef

string还能以一个C风格字符串为初值,还可以转换成一个C风格字符串(这里的语法还没有涉及,不作要求,如有需要须强行记忆,可了解面向对象编程的构造函数成员函数等知识)

1
2
3
4
5
6
char s1[10]={'a','b','c'};
string s2(s1);//将C风格的s1作为string s2构造函数的参数进行初始化
cout<<s2;
s2="hello";
strcpy(s1,s2.c_str());//调用s2的成员函数,返回一个C风格的字符串
puts(s1);

输出:
abc
hello

这样,我们可以通过两种字符串操作的转化实现按需求灵活使用。

string类型的成员函数

成员函数这个概念在学习面向对象编程之前还有再放一放,我们只需知道,string类型和C风格一样使用函数支持很多的字符串操作,只是并不是使用C语言中(也就是我们之前学过)的那种函数。
比如,类似于strlen函数,string类型则有:

1
2
string s1="apple";
cout<<s1.length();

输出:
5

成员函数的调用方式如上。

由于string类型的面向对象特性已超出本教程的范围,更多的string类型成员函数有余力可自行查询。

作业

思考

1、什么是字符串,字符串和字符型数组有什么共同点?有什么区别?
2、C风格的字符串和C++风格的字符串有什么区别?
3、区分头文件string.h、cstring、string

习题

1、密码
2、表达式括号匹配
*3、实现一个能计算一位正整数+、-运算表达式的计算器
例:
输入:
1+3+2-5-7
输出:
-6
**4、实现一个能计算一位正整数+、-、*、/运算表达式的计算器
例:
输入:
1+3*5-4
输出:
12
***5、实现一个能计算正整数+、-、*、/运算表达式的计算器
例:
输入:
12+3*5-45
输出:
-18