1、串的基本概念
(1)、串中字符的数目n称作串的长度,所以零个字符的串称为空串
(2)、两个串相等的条件是长度相等、对应各个位置上的字符相等时才相等
(3)、空格也是字符,所以由空格构成的字符串不是空串,是空格串
2、字符串与字符数组
字符数组名是地址常量,所以不能通过赋值语句将字符串常量或者其他字符数组中的字符串直接赋值给字符数组!(常量不能被赋值),下面的是错误的操作
char c[12],s[12];
c="vbvbvbv";
s=c; //错误的操作
(1)、字符数组并不等同于字符串变量
(2)、编译系统给出存放每个字符串常量的存储空间的首地址,从这个首地址开始,在遇到第一个“\0”的时候,表示字符串的结束,由它前面的字符组成字符串
(3)、对于字符串常量,C编译系统会自动在末尾加一个“\0”作为结束符,例如:字符串常量“HELLO”共有5个字符,即其长度为5,但是在内存中占了6个字节的长度,最后一个字节存储的是“\0”
(4)、字符数组的每个元素可以存放一个字符,但是并不要求它的最后一个字符必须是字符必须为字符串结束标志“\0”。而与字符串有关的大量操作都与字符串结束标志“\0”有关,因此只有在字符数组中有效字符后面加上“\0”这一特定情况下。才可以把这种字符数组“看作”字符串变量!
(5)、所以字符数组可以用来存放字符串,所以可以使用字符串常量来为字符数组初始化,例如
char c[20] = {“computer & C”};
数组名是字符串的首地址,所以可以习惯上省略花括号,简写成
char c[20] = “computer & C”;
由于“computer & C”
是字符串常量,所以系统会自动加入字符串结束标志“\0”,所以不必人为加入,此时应该数组越界。下面的写法是错误的,因为数组大小只有12,并且字符串的长度是12,所以系统自动加入字符串结束标志“\0”会引起数组越界的错误
char c[12] = “computer & C”;
所以为了避免上面的这种问题,所以可以使用下面的这种方式定义
char c[] = “computer & C”; //数组大小为13,但是字符串的长度只有12
等价于
char c[] = {'c','o','m','p','u','t','e','r','','&','','c','\0'};
此时,是通过逐个元素赋初值来实现字符串的初始化,所以必须在字符数组后面加上“\0”,如果没有这个“\0”,则是不能当成字符串来使用的;若误当成字符串来使用,系统会在其后的内存中找一个距离它最近的的“\0”来作为字符串结束标志,可这个能导致错误的的结果。下面的初始化默认c[12]为“\0”,因此可以将c当成字符串来使用
char c[13] = {'c','o','m','p','u','t','e','r','','&','','c'}; //数组大小只要大于13即可
3、字符串的基本操作
下面串的各种操作是基于结构体,但是我们也可以不使用结构体,定义字符串常量或者字符数组也能完成串的各种操作。所以在这种情况下没有结构体使用方便,因为结构体中有串的长度,所以使用普通方法不能知道串的长度,在串连接等操作的时候会不方便,所以我们要定义一个求字符串长度的函数。在操作开始之前先通过求字符串长度的函数得到字符串长度,再执行接下来的操作会很方便!但是注意定义数组操作的时候一定要注意字符数组越界问题!
(1)、输入串、打印串
#include<string.h>
#include<stdio.h>
#define Maxsize 255
#include<iostream>
using namespace std;
typedef struct{
char str[Maxsize+1];
int length;
}String;
//输入串
int StrAssign(String &S,char input[])
{
int i = 0;
while (input[i] != '\0') //循环,将字符串常量的值赋值给S
{
S.str[i] = input[i];
i++;
}
S.length = i; //将i赋值给S的长度
return 0;
}
//打印串
int PrintString(String S){
if (S.length == 0)
{
cout<<"\n当前的串为空!"<<endl;
return 0;
}
cout<<"\n长度为 "<<S.length<<" 的串打印如下:";
puts(S.str);
return 0;
}
//打印串1
int PrintString1(String S) //打印串S
{
if (S.length == 0)
{
cout<<"\n当前的串为空!"<<endl;
return 0;
}
int i;
cout<<"\n长度为 "<<S.length<<" 的串打印如下:";
while(S.str[i] != '\0'){
cout<<S.str[i];
i++;
}
return 0;
}
int main(){
String S; //主串
String sub; //子串
char str[Maxsize]; //定义字符数组
cout<<"\n请输入主串:";
gets(str); //系统自动给输入串加"\0"
StrAssign(S,str);
PrintString(S);
PrintString1(S);
}
(2)、串连接
#include<string.h>
#include<stdio.h>
#define Maxsize 255
#include<iostream>
using namespace std;
typedef struct{
char str[Maxsize+1];
int length;
}String;
//输入串
int StrAssign(String &S,char input[])
{
int i = 0;
while (input[i] != '\0') //循环,将字符串常量的值赋值给S
{
S.str[i] = input[i];
i++;
}
S.length = i; //将i赋值给S的长度
return 0;
}
//连接成一个新的串
int ConcatString(String &S, String S1, String S2) //用S返回由S1和S2连接而成的新串
{
int i=0, j=0;
while(S1.str[i] != '\0'){
S.str[i] = S1.str[i];
i++;
}
S.length = S1.length;
while(S2.str[j] != '\0'){
S.str[S.length] = S2.str[j];
S.length++;
j++;
}
return 0;
}
//将串二连接到串一
int ConcatString1(String &S1, String S2) //将S2连接到S1后面
{
int i=0;
while(S2.str[i] != '\0'){
S1.str[S1.length] = S1.str[i];
S1.length++;
i++;
}
return 0;
}
//打印串
int PrintString(String S){
if (S.length == 0)
{
cout<<"\n当前的串为空!"<<endl;
return 0;
}
cout<<"\n长度为 "<<S.length<<" 的串打印如下:";
puts(S.str);
return 0;
}
int main(){
String S; //合并主串1和主串2
String S1; //主串1
String S2; //主串2
char str1[Maxsize]; //输入S1
cout<<"\n请输入主串S1:";
gets(str1); //系统自动给输入串加"\0"
char str2[Maxsize];
cout<<"\n请输入主串S2:"; //输入S2
gets(str2);
StrAssign(S1,str1);
StrAssign(S2,str2);
PrintString(S1);
PrintString(S2);
ConcatString(S,S1,S2);
cout<<"\nS1和S2合并为S如下:\n"; //输入S2
PrintString(S); //连接成一个新串
cout<<"\n将S2连接到S1的后面如下:\n"; //输入S2
ConcatString1(S1,S2);
PrintString(S1); //将S2连接到S1的后面
}
当然c中还提供了字符串连接函数strcat(字符数组名1,字符串数组名2/字符串2),并删除字符串1的标志“\0”,结果字符串的长度是两个字符串长度之和。第二个参数可以是数组名,也可以是字符串常量,但是第一个参数一定是字符数组名。函数反回值是字符数组1的首地址!
#include<string.h>
#include<stdio.h>
#define Maxsize 255
#include<iostream>
using namespace std;
main(){
// char *p ="cvcvcvc";
// char *s ="vhvhvhvhh";
// strcat(p,s);
// cout<<"\n通过指针将s指向的字符串赋值连接到指针p所指向的字符串后面"<<p; 操作失败
cout<<"\n\n--------------------------------------------------------------"<<endl;
cout<<"\n请输入字符串str1:";
//char *str1;这种写法不能使strl指针指向从控制台输入的字符串,操作失败
char str1[50]; //str1必须是字符数组
gets(str1);
cout<<"\n\n请输入字符串str2:";
//char *str2;操作失败
char str2[50]; //str2可以是字符数组
gets(str2);
strcat(str1,str2); //str1必须是字符数组
cout<<"\n将字符串str2连接到字符串str1:"<<str1;
cout<<"\n\n--------------------------------------------------------------"<<endl;
char *str3 ="是str3"; //str3可以是字符串常量
strcat(str1,str3); //str1必须是字符数组
cout<<"\n\n将指针str3指向的字符串常量连接到字符串str1:"<<str1;
cout<<"\n\n--------------------------------------------------------------"<<endl;
char str4[50] ="是str4"; //str4可以是字符数组
strcat(str1,str4); //str1必须是字符数组
cout<<"\n\n将字符数组str4中的字符串常量连接到字符串str1:"<<str1;
}
(3)、取子串
#include<string.h>
#include<stdio.h>
#define Maxsize 255
#include<iostream>
using namespace std;
typedef struct{
char str[Maxsize+1];
int length;
}String;
//输入串
int StrAssign(String &S,char input[])
{
int i = 0;
while (input[i] != '\0') //循环,将字符串常量的值赋值给S
{
S.str[i] = input[i];
i++;
}
S.length = i; //将i赋值给S的长度
return 0;
}
//打印串
int PrintString(String S){
if (S.length == 0)
{
cout<<"\n当前的串为空!"<<endl;
return 0;
}
cout<<"\n长度为 "<<S.length<<" 的串打印如下:";
puts(S.str);
return 0;
}
//BF算法求模式串中第一个字符在主串中出现的位置
int IndexBF(String S,String T,int Position){
}
//求子串,使用SS返回子串
void GetSubString(String S,String &SS,int SP,int L){
if((SP > S.length) || (L < 0) || (L + SP -1 > S.length) || (SP < 1)){
cout<<"\n子串范围越界!\n"<<endl;
}
int i,j=0;
for(i = SP;i < SP + L;i++){
SS.str[j] = S.str[i];
j++;
}
SS.length = L;
}
int main(){
int StartPosition,Length;
String S; //主串
String SubString; //使用SubString得到子串
char str[Maxsize]; //定义字符数组
cout<<"\n请输入主串:";
gets(str); //系统自动给输入串加"\0"
StrAssign(S,str);
cout<<"\n请输入想要返回的串的开始位置以及长度:";
cin>>StartPosition;
cout<<" ";
cin>>Length;
GetSubString(S,SubString,StartPosition,Length);
cout<<"\n子串如下:\n";
PrintString(SubString);
}
(4)、串比较
(1)、比较串的长度
//求串长
int LengthString(char str[]){
int i = 0;
while(str[i]!='\0'){
i++;
}
return i;
}
(2)、比较串的内容是否相同
#include<string.h>
#include<stdio.h>
#define Maxsize 255
#include<iostream>
using namespace std;
typedef struct{
char str[Maxsize+1];
int length;
}String;
//输入串
int StrAssign(String &S,char input[])
{
int i = 0;
while (input[i] != '\0') //循环,将字符串常量的值赋值给S
{
S.str[i] = input[i];
i++;
}
S.length = i; //将i赋值给S的长度
return 0;
}
//比较串
void CompareString1(String S1,String S2){
if (S1.length != S2.length){
cout<<"\n两个串长度不相同!\n";
}
else{ //两个串长度相同
int i = 0;
while(S1.str[i] != '\0'){
if(S1.str[i] != S2.str[i]){
cout<<"\n两个串内容不相同!不相同发生在第 "<<i+1<<" 个字符\n";
break;
}
i++; //i为S1.length的时候不满足条件,退出循环
}
//没有if语句会按照程序的执行逻辑先打印 cout<<"\n两个串内容不相同!\n",后打印
//cout<<"\n两个串内容完全相同!\n";如果有if语句就不会发生模棱两可的打印结果
if(i == S1.length)
cout<<"\n两个串内容完全相同!\n";
}
}
int CompareString2(String S1,String S2)
{ //若S1 = S2,则返回0
int i = 0; //若S1 > S2,则返回1
while ((i < S1.length ) && (i < S2.length)) //若S1 < S2,则返回-1
{
if (S1.str[i] > S2.str[i]) //单个字符归根到底是比较ASSLL码值,所以不需要强转类型
return 1; //退出循环并且带回返回值
if (S1.str[i] < S2.str[i])
return -1;
if (S1.str[i] == S2.str[i])
{
++i;
continue; //如果两字符相等,则退出该次循环,进行下一次循环
}
++i;
}
if ((i == S1.length) && (i !=S2.length))
return -1;
else if ((i != S1.length) && (i = S2.length))
return 1;
else
return 0;
}
int CompareString3(String S1,String S2){
for(int i=0;i <S1.length && i <S2.length ;i++){
if(S1.str[i] != S2.str[i]){ //若两个字符不相等,看在不相等的字符地方,谁的ASSLL码值大,看差值即可知道大小
return S1.str[i] - S2.str[i];
}
}
//扫过相同的字符之后,根据情况返回
return S1.length - S2.length;
}
//打印串
int PrintString(String S){
if (S.length == 0)
{
cout<<"\n当前的串为空!"<<endl;
return 0;
}
cout<<"\n长度为 "<<S.length<<" 的串打印如下:";
puts(S.str);
return 0;
}
int main(){
String S1;
String S2;
char String1[50];
char String2[50];
cout<<"\n请输入String1:";
gets(String1);
StrAssign(S1,String1);
PrintString(S1);
cout<<"\n请输入String2:";
gets(String2);
StrAssign(S2,String2);
PrintString(S2);
cout<<"\n--------------------------------------------方式一-----------------------------------------------\n";
CompareString1(S1,S2);
cout<<"\n--------------------------------------------方式二-----------------------------------------------\n";
if(CompareString2(S1,S2) == 0)
cout<<"\n两个串完全相同!\n";
if(CompareString2(S1,S2) == 1)
cout<<"\nString1大于String2!\n";
if(CompareString2(S1,S2) == -1)
cout<<"\nString2大于String1!\n";
cout<<"\n--------------------------------------------方式三-----------------------------------------------\n";
if(CompareString3(S1,S2) == 0)
cout<<"\n两个串完全相同!\n";
if(CompareString3(S1,S2) > 0)
cout<<"\nString1大于String2!\n";
if(CompareString3(S1,S2) < 0)
cout<<"\nString2大于String1!\n";
}
当然c中同样有和上面的程序一样功能的函数strcmp(字符串1,字符串2)来完成,其中,字符串1和字符串2可以是字符数组名或者是字符串常量!函数返回值是一个int类型。
strcmp(字符串1,字符串2)
字符串1等于字符串2,返回值为0;
字符串1大于字符串2,返回值为一个正数;
字符串1小于字符串2,返回值是一个负数;
4、总结
c语言中还提供了strcpy(字符数组名1,字符串2/字符数组名),将字符串2连同结束标志符‘\0’复制到字符数组1中。其中,字符串2可以是字符串常量,也可以是字符数组,当字符串2是一个字符串常量的时候,相当于把一个字符串赋予了一个字符数组。
还有strlen(字符串),计算字符串的实际长度,不包括‘\0’
通过前面的例子可以知道对于字符串的操作中有两个量一定不可以忽视,否则编写程序就会有问题。‘字符串结束标志符号\0’和字符串的实际长度,这两个量对于循环遍历操作字符串尤为方便!
如果没有使用顺序表或者链表定义的,由于不能得到串的长度,可以使用下面的方式得到当前串的长度
//求串的实际长度
int LengthString(char str[]){
int i = 0;
while(str[i]!='\0'){
i++;
}
return i;
}