C语言笔记:字符串处理相关问题

原文链接: http://c.biancheng.net/c/

在C语言中,字符串就是字符数组,因此可以像处理普通数组一样处理字符串,唯一的区别就是字符数组中的字符串是以"\0"作为字符串结束标记的。

统计字符数(仅支持小写)

问题描述:
判断一个由a~z这个26个小写英文字符组成的字符串中哪个字符出现的次数最多。输入数据占1行,是一个由a~z这26个小写英文字符组成的字符串,不超过1000个字符且非空。输出1行,输出1行,包括出现次数最多的字符和该字符出现的次数(中间用空格隔开)。

问题分析:由于字母在ASCII表中时连续的,因此可以用一维数组count[26]统计每个字母出现的次数,count[0]表示字母a出现的次数,count[1]表示字母b出现的次数,以此类推。首先遍历输入字符串的每一个字符,是第i个字母,就让count[i]的值自增1(count数组全部初始化为0),之后再求出该数组最大值下标,再转化为字符类型输出即可。

代码:

#include <stdio.h>
#include <string.h>
int main(void)
{
 char str[1001];//字符串的字符个数最多1000
 int count[26]={};//a[0]~a[25]的元素(整型) 分别表示a~z字符出现次数
 printf("请输入一个英文字符串:");
 scanf("%s",str);//计算字符串的长度
 int len=strlen(str);
 for(int i=0;i<len;i++)
 {
  count[str[i]-'a']++; 
 } 
 int maxIndex=0;//存放最大值数组下标,默认第一个元素的下标是最大值下标 
 for(int i=1;i<26;i++)
 {
  if(count[i]>count[maxIndex])
  {
   maxIndex=i;
  }
 }
 printf("%c %d\n",maxIndex+'a',count[maxIndex]);
 return 0;
}

输出:
在这里插入图片描述
补充:

  • C语言字符类型用关键字char表示,它实际存储的是字符的ASCII码,字符常量用单引号引起来,在语法上可以把字符类型当成int型使用
  • 程序中也可以不计算字符串长度来遍历数组,利用字符串的结束标志“\0”,可以把
int len=strlen(str);
for(int i=0;i<len;i++)
{
 count[str[i]-'a']++; 
} 

改成

for(int i=0;str[i];i++)
{ 
 count[str[i]-'a']++;
}

支持统计大小写

由于英文字母有大小写,这里进行改进一下

#include <stdio.h>
#include <string.h>
int main(void)
{
 char str[1001];//字符串的字符个数最多1000
 int count[2][26]={};//a[0]~a[25]的元素(整型),行标为0分别表示a~z字符出现次数;行标为1分别表示A~Z字符出现次数
 printf("请输入一个英文字符串:");
 scanf("%s",str);//计算字符串的长度
 int len=strlen(str);
 for(int i=0;i<len;i++)
 {
  int l_flag;//设置行标是0还是1 
  if(str[i]>='a'&&str[i]<'z')
  {
   l_flag=0;
   count[l_flag][str[i]-'a']++;
  }
  if(str[i]>='A'&&str[i]<'Z')
  {
   l_flag=1;
   count[l_flag][str[i]-'A']++;
  }
 }
 int row=0,col=0;//存放最大值数组下标,默认二维数组第一个元素的下标是最大值下标 
 for(int i=0;i<2;i++)
 {
  for(int j=0; j<26;j++)
  {
   if(count[row][col]<count[i][j])
   {     /*找最大值的过程*/
    row=i;
    col=j;
   }
  }
 }
 if(row==0)
 {
  printf("%c %d\n",col+'a',count[row][col]);
 }
 else
 {
  printf("%c %d\n",col+'A',count[row][col]);
 }
 return 0;
}

输出:
在这里插入图片描述
在这里插入图片描述

被3整除

我们知道一个int型能存储的最大数是2的31次方,即2^(31)-1=2147483648 - 1 = 2147483647,如果我们要进行的运算数大于这个值就不能用int存储,当然,内置数据类型long,longlong 使我们存储运算的数更大,但这里我们提供另外一种思路,即用字符串来表示这个大整数,串中的每一个字符对应大整数的一位,我们在访问字符串各字符时要注意把数字字符转换为数字。这里我们来判断输入的几个大整数数据判断它们是否能被3整除。

代码如下:

#include <stdio.h>
#include <string.h>
int main(void)
{
 int n;//待输入的数据组数
 char str[101];//设置输入的大整数最大不超过100位
 printf("请输入数据的组数:\n");
 scanf("%d",&n);
 int count=1;
 while(n--)
 {
  int sum=0;
  printf("请输入第%d个数:",count++);
  scanf("%s",&str);//输入大整数存放到字符串str中
  int len=strlen(str); 
  for(int i=0;i<len;i++)
  {
   sum+=str[i]-'0';//字符类型数字强制转换为整型,然后每一位的数字相加 
  }
  if(sum%3==0)//这里的sum是各个位上的数相加和 
  {
   printf("YES");
  }
  else
  {
   printf("NO\n");
  }
 } 
 return 0;
}

输出:
在这里插入图片描述

解释:
为什么要以字符串的形式存放整数的各个位,这与题目相关,这数学上有一个定理,如果一个整数各位和能够被3整除,则此整数即能被3整除

删除单词后缀

问题描述:
给定一个单词,如果该单词以er、ly或者ing后缀结尾,则删除该后缀(需要保证删除后缀额单词长度不为0),否则不进行任何操作。

法一:

#include <stdio.h>
#include <string.h>
int main(void)
{
 char word[33],sub[4]={'\0'};
 printf("请输入一个单词:"); 
 scanf("%s",word);
 int len=strlen(word);//求单词长度len
 if(len>2)//输入的单词字母数必须大于2才判断 
 {
  if(word[len-2]=='e'&&word[len-1]=='r')
  {
   word[len-2]='\0';
  }
  if(word[len-2]=='l'&&word[len-1]=='y')
  {
   word[len-2]='\0';
  }
 } 
 if(len>3)
 {
  if(word[len-3]=='i'&&word[len-2]=='n'&&word[len-2]=='g')
  {
   word[len-3]='\0';
  }
 }
 printf("去除后缀的结果为:%s\n",word);
 return 0;
} 

法二:

#include <stdio.h>
#include <string.h>
int main(void)
{
 char word[33],sub[4]={'\0'};
 printf("请输入一个单词:"); 
 scanf("%s",word);
 int len=strlen(word);//求单词长度len
 if(len>2)//考虑是否有"er"或者"ly"后缀 
 {
  strncpy(sub,word+len-2,2);//将单词word最后两个字母(最后一个参数指定复制多少个字节)复制到sub指向的首地址中 
  if(strcmp(sub,"er")==0||strcmp(sub,"ly")==0)//单词有后缀"er"或者"ly"
  {
   word[len-2]='\0';//去掉原数组的后两位,即移动字符串结束标记即可 
  }
 } 
 if(len>3)//考虑是否有"ing"后缀 
 {
  strncpy(sub,word+len-3,3);
  if(strcmp(sub,"ing")==0)
  {
   word[len-3]='\0';
  }
 }
 printf("去除后缀的结果为:%s\n",word);
 return 0;
} 

输出:
在这里插入图片描述

查找字符串中的单词

问题描述:
给定一个单词,输出它在给定句子中出现的次数和单词首字符第一次出现的字符位置(下标从1开始)。注意:不匹配大小写,但要求完全匹配,即给定单词必须与句子中的某一独立单词在不区分大小写的情况下完全相同,如果给定单词仅仅是文章中某一单词的一部分则不算匹配

问题分析:

  • 需要把句子中的单词转换为大写或者小写,这里设计了ToLower(char *s)统一转换为小写,大小写字母间的ASCII码值相差32(‘a’-‘A’=32)
  • 要获取句子中的每一个单词,使用C语言自带库函数strtok,函数原型如下:
char * strtok(char *str,const char *delimiters)
参数名 含义
str 待分割的字符串
delimiters 分隔符字符串(待分割字符串中的每个字符均可作为分隔符)
  • 因为句子中用空格分隔每个单词,故用gets()函数获取字符串,不使用scanf()
#include <stdio.h>
#include <string.h>
void ToLower(char *s);//将字符串统一转换为小写,方便比较 
char s1[1000000],s2[1000000];
int main(void)
{ 
 printf("请输入一行英语短语:\n");
 gets(s2);
 ToLower(s2);
 printf("请输入一个带查找英语单词:\n");
 gets(s1);
 ToLower(s1);
 int t=0;
 char *pch;
 int firstPos;
 pch=strtok(s2," ");//该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。
 while(pch!=NULL ) //每循环一次,pch依次指向字符串的第二个子串、第三个子串,等等 
 {
  if(strcmp(pch,s1)==0)//每次循环所得到的的子串都要进行一次比较 
  {
   t++;//若找到,计数器自增1
   if(t==1)//如果第一次找到 
   {
    firstPos=pch-s2;
   } 
  }
  pch=strtok(NULL, " ");//此处我暂时不理解,脑子短路了,呵呵,不过通用写法是这样 
 }
 if(t>0)//若有找到的记录
 {
  printf("出现次数为:%d ----第一次匹配单词在所属句子中的第%d个字符\n",t,firstPos+1);
 }
 else
 {
  printf("-1\n");
 } 
 return 0;
}
void ToLower(char *s)
{
 int len=strlen(s);
 for(int i=0;i<len;i++)
 {
  if(s[i]>='A'&&s[i]<='Z')
  {
   s[i]+=32;
  } 
 } 
}    

输出:
在这里插入图片描述

字符串加密

问题描述:
现要对一个由大写字母组成的字符串进行加密,用以下两种方法对字符串进行两次加密

  • 替换法:把一个字母替换成它之后的第k个字母,如AXZ,k=2,加密后CZB(Z之后第二个字母为B)
  • 置换法:改变原来字符串中字符的顺序,如将顺序<2 3 1>应用于ABC加密得到BCA,将原来第2个字符移到第一位,原来第3个字符移到第二位,原来第1个字符移到第三位
#include <stdio.h>
#include <string.h>
int main(void)
{
 const char table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// int len=strlen(table);
 char str[100],t[100];//分别第一次加密字符串(还要被第二次加密操作)和第二次加密字符串(最终结果)
 int k;//替换法所要用到的变量 
 int n[100];//存放置换法,改变字符串中字母的顺序 
 while(printf("请输入一个只包含大写字母的字符串:"),scanf("%s",str) != EOF)//多组测试,用Ctrl+Z结束程序 
 {
  int len=strlen(str);
  printf("请输入替换法的k变量:");
  scanf("%d",&k);
  printf("请输入置换法的数字序列:");
  for(int i=0;i<len;i++)
  {
   scanf("%d",&n[i]);
  }
  for(int i=0;i<len;i++)//第一次用替换法加密 
  {
   str[i]=table[(str[i]-'A'+k)%26];
  }
  for(int i=0;i<len;i++)
  {
   t[i]=str[n[i]-1];//按指定的顺序重新排列字母 
  }
  t[len]='\0';////字符数组若不是在定义时以字符串整体赋值而是单个元素赋值,系统不会在加上字符串结束符'\0' 
  printf("加密后的字符串为:%s\n\n",t);
 }
 return 0; 
} 

输出:

在这里插入图片描述

难点解读:
在替换法中,字母’Z’的下2个字母是B,可以用求模运算表达式

str[i]=table[(str[i]-'A'+k)%26]

过滤多余的空格

问题描述:
一个英语句子的单词中间也许有多个连续的空格,此时需要过滤到多余的空格,只留下一个空格

#include <stdio.h>
#include <string.h>
int main(void)
{
 char str[200];
 gets(str);
 int i=0,j=0;
 int flag=1;//区分空格类型,若是单词后的第一个空格则flag=1,否则flag=0 
 while(str[j])
 {
  if(str[j]!=' ')
  {
   str[i++]=str[j];
   flag=1;//遍历输入的字符串(含空格),若字母是非空格字符,则flag始终为1
  }
  else if(str[j]==' '&&flag)//字符串后的第一个空格一定满足flag=1(前面都是非空格字符)并且当前字符为空格字符 
  {
   str[i++]=str[j];
   flag=0;//若该空格后面还有空格,else if代码段不继续执行(相当舍弃多余的空格) 
  }
  j++;
 }
 str[i]='\0';
 printf("%s",str);
 return 0;
}

输出:
在这里插入图片描述

ISBN(International Standard Book Number)国际标准书号

什么是ISBN:

国际标准书号(International Standard Book Number),简称ISBN,是专门为识别图书等文献而设计的国际编号。2007年1月1日前,ISBN由10位数字组成,分四个部分:组号(国家、地区、语言的代号),出版者号,书序号和检验码。2007年1月1日起,实行新版ISBN,新版ISBN由13位数字组成,分为5段,即在原来的10位数字前加上3位EAN(欧洲商品编号)图书产品代码“978”。13位书号与10位书号的计算方法不同,具体可以查看百度百科或者http://www.systron.com.cn/tiaoxingma/bookbarcode8.htm

问题描述:
每一本正式出版的图书都有一个ISBN号码与之对应,新版ISBN其固规定格式为978-x-xxx-xxxxx-x,其中符号"-"是分隔符,各组数字段的含义如下:

第一组 第二组 第三组 第四组 第五组
978或979 国家、语言或区位代码:7代表中国大陆 出版社代码:由各国家或地区的国际标准书号分配中心,分给各个出版社 书序码:该出版物代码,由出版社具体给出。出版社的规模越大,出书越多,其号码就越短。 校验码:只有一位,从0到9

程序要求输入一个13位ISBN码,判断其中的标识码是否正确,若正确,则输出Right,否则完整输出正确的ISBN码

13位ISBN书号的识别码计算例子为:
前12位数依次乘以1和3,然后求它们的和除以10的余数,最后用10减去这个余数,就得到了校验码。如果余数为0,则校验码为0.
比如,7-5600-3879-x在13位ISBN中,就是978-7-5600-3879-x。它的校验码计算方法如下
9x1+7x3+8x1+7x3+5x1+6x3+0x1+0x3+3x1+8x3+7x1+9x3

		    = 9+21+8+21+5+18+0+0+3+24+7+27

		    = 143

		    = 140+3

		    = 14x10+3
		  10-3=7

所以,在13位ISBN中,这本书的校验码x应该为7

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int main(void)
{
	char str[20];
	scanf("%s",str);
	bool flag=true;
	int sum=0;
	for(int i=0;i<15;i++)
	{
		if(str[i]>='0'&&str[i]<='9')//"-"字符不进入运算 
		{
			int n=(str[i]-'0');//依次获得每一组数字段的每一个数字,用1分别乘ISBN的前12位中的奇数位(从左边开始数起),用3乘以偶数位
			if(flag)
			{
				sum+=n*1;
				flag=false;
			}
			else
			{
				sum+=n*3;
				flag=true;
			}
		}
	}
	int mark=10-(sum%10);//乘积之和以10为模做求模运算 ,最后用10减去该余数得到校验值mark
	int len=strlen(str);
	if(mark==(str[len-1]-'0'))//将正确求得的校验码与我们输入的校验码比较 
	{
		printf("Right!");
	}
	else
	{
		str[len-1]=mark+'0';//修改字符数组最后一个元素,然后输出
		printf("正确13位ISBN码为:");
		for(int i=0;i<len;i++)
		{
			printf("%c",str[i]);
		} 
	}
	return 0;
}

输出:

在这里插入图片描述

提取数字

问题描述:
输入一个字符串,内有数字字符和非数字字符,请找出字符串中所有由连续数组字符组成的正整数,并按出现顺序输出。

法一

#include <stdio.h>
#include <string.h> 
int main(void)
{
 int shu[1000];//shu数组的每一个元素即是从str中提取的一个连续的数字字符,只不过它们变为整型 
 char str[500];//用户输入的的一段字符串 
// scanf("%s",str);
 gets(str);
 int k=0;//操作shu数组的下标 
 for(int i=0;i<strlen(str);i++)//字符串扫描 
 {
  if(str[i]>='0'&&str[i]<='9')//遇到字符串是数字类型字符串,开始提取整数 
  {
   int sum=str[i]-'0';//字符型数字转换为整型数字 
   int j=i+1;//对扫描到的第一个数字字符的之后字符进行循环判断 
   while(j<strlen(str)&&str[j]>='0'&&str[j]<='9')//继续直到串尾或遇到非数字 
   {
    sum=sum*10+(str[j]-'0');
    j++;//符合数字字符,数字字符下标变量自增1 
   }
   shu[k++]=sum;//修改i的下标位置的字符  
   i=j;//修改i值,便于下一次for循环跳过读while循环取完的数字字符下标 
  }
 }
 for(int i=0;i<k;i++)//k的值表示在shu数组中保存有多少个提取完的连续字符 
 {
  printf("%d\n",shu[i]);
 }
 return 0;
}

输出:
在这里插入图片描述

疑难解答:下面的代码即是解决输出整数不能输出多余的0,123=((0*10+1)*10+2)*10+3

sum=sum*10+(str[j]-'0');

法二

#include <string.h>
#include <stdio.h>
#include <stdlib.h>//atoi()
void Output(char str[]);
int main(void)
{
 int shu[1000];
 char str[500];
 gets(str);
 int k=0;
 int len=strlen(str);
 for(int i=0;i<len;i++)//所有非数字字符用空格替换 
 {
  if(str[i]<'0'||str[i]>'9')
  {
   str[i]=' ';
  }
 }
 Output(str);
 char *pch=strtok(str," ");//用空格分隔字符串 
 while(pch)
 {
  shu[k++]=atoi(pch);//atoi()把数字字符串转换成整数,存储到shu数组中 
  pch=strtok(NULL," ");//下一个子串 
 }
 for(int i=0;i<k;i++)
 {
  printf("%d\n",shu[i]);
 }
 return 0;
} 
void Output(char str[])
{
 for(int i=0;i<strlen(str);i++)
 {
  printf("%c",str[i]);
 }
 printf("(测试输出)\n");
}

输出:
在这里插入图片描述
疑难解答:

char * strtok(char * str,const char *分隔符);

参考:
http://www.cplusplus.com/reference/cstring/strtok/?kw=strtok

字母重排

问题描述:
输入一行字符串,里面包含数字,字母(小写)等各种字符,要求把字符串里面的字母给挑出来,并按照ASCII码从小到大排序

法一

解决思路:
将用户输入的字符串提取出小写字母字符保存到新的字符数组,然后使用冒泡排序法对这个新数组按ASCII升序排序,最后输出

#include <stdio.h>
#include <string.h>
void Output(char str[]);
void SortASCII(char str[]);
void Swap(char *pa,char *pb);//交换pa和pb所指变量的值 
int main(void)
{
 char res[100];//保存提取的字符串 
 char str[100];//用输入 
 scanf("%s",str);
 int len=strlen(str);
 int k=0;
 for(int i=0;i<len;i++)
 {
  if(str[i]>='a'&&str[i]<='z')
  {
   res[k]=str[i];
   k++;
  }
 }
 SortASCII(res);
 Output(res);
 return 0;
}
void Output(char str[])
{
 for(int i=0;i<strlen(str);i++)
 {
  printf("%c",str[i]);
 }
}
void SortASCII(char str[])
{
 for(int i=1;i<strlen(str);i++)//控制轮数 
  {
  for(int j=0;j<=strlen(str)-1-i;j++)//每一轮对于无序区进行比较 
    {
      if(str[j]>str[j+1])
      {
       Swap(&str[j],&str[j+1]); 
      }
    }
  } 
}
void Swap(char *pa,char *pb)
{
 char temp;
 temp=*pa;
 *pa=*pb;
 *pb=temp;
}

输出:
在这里插入图片描述

法二

解决思路:用一个数组a[26]来标记用户输入字符数组str[500]中的小写字母以及个数,最后直接按顺序输出a[26],注意有多个相同的小写字母要重复输出

#include <stdio.h>
#include <string.h>
int main(void)
{
 char str[500];
 scanf("%s",str);
 int a[26]={0};//数组a的0-25下标元素对应a~z26个小写字母
 int len=strlen(str);
 for(int i=0;i<len;i++)
 {
  if(str[i]>='a'&&str[i]<='z')
  {
   a[str[i]-'a']++;//数组a记录用户输入字符数组str的所有小写字母及其个数 
  } 
 } 
 for(int i=0;i<26;i++)
 {
  for(int j=0;j<a[i];j++)//a[i]存放小写字母出现的次数 
  {
   printf("%c",i+'a');//
  }
 } 
 return 0;
}

输出:
在这里插入图片描述

字符串判等

问题描述:
给定两个由大小写字母和弄个组成的字符串s1和s2,它们的长度都不超过100个字符,长度也可以为0.判断压缩掉空格并忽略大小写后,这两个字符串是否相等,若两个字符串相等则输出YES,否则输出NO

#include<string.h>
#include<stdio.h>
void DelSpace(char *s);//删除字符串中的空格 
void LowtoUpper(char *s);//字符串小写变大写 
int main(void)
{
 char str1[256],str2[256];
// printf("请输入第一个字符串:");
 gets(str1);
// printf("请输入第二个字符串:");
 gets(str2);
 LowtoUpper(str1);
 LowtoUpper(str2);
 DelSpace(str1);
 DelSpace(str2);
 if(strcmp(str1,str2)==0)
 {
  printf("YES\n");
 }
 else
 {
  printf("NO\n");
 }
 return 0;
}
void DelSpace(char *s)
{
 int k=0;
 int i=0;
 while(s[i])
 {
  if(s[i]!=' ')
  {
   s[k++]=s[i];
  }
  i++;
 }
 s[k]='\0';
}
void LowtoUpper(char *s)
{
 int i=0;
 while(s[i])
 {
  if(s[i]>='a'&&s[i]<='z')
  {
   s[i]-=32;
  }
  i++;
 }
}

输出:
在这里插入图片描述

判断一个字符串是否为回文

问题描述:
输入一个字符串,判断输出是否为回文,回文子串即从左往右输出和从右往左输出结果是一样的

#include <stdio.h>
#include <string.h>
void huiwen(char *s);//判断字符串是否为回文 
int main(void)
{
	char s[501],subs[501];//s为用户输入的字符串,subs为分割的回文子串 
	printf("请输入一个字符串:");
	scanf("%s",s);
	huiwen(s);
	return 0;
}
void huiwen(char *s)
{
	int j=0,i=strlen(s)-1;
	for(;j<=i;i--,j++)
	{
		if(s[i]!=s[j])
		{
			break;
		}
	}
	if(j<=i)
	{
		printf("%s不是回文串\n",s);
	}
	else
	{
		printf("%s是回文串\n",s);
	}

}

输出:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42124234/article/details/102568958