字符串输入输出函数和字符串处理函数

目录

前言 

字符串的输入和输出 

scanf()

printf()

gets()和 puts()

fgets()和fputs

字符串处理函数

strlen()

strcat()和 strncat()

strcmp()和 strncmp()

strcpy( )和 strncpy()

strchr()、strrchr()和 strstr()

strpbrk()


前言 

        c 语言的标准库提供了很多非常有用的字符串处理函数。它们都要求以字符串作为参数,并且它们都返回整数值或指向 char 的指针。在头文件 stdio.h 和 string.h中给出了字符串处理函数原型,所以使用这些字符串处理函数时要引用相应的头文件。文章有点长,有需要请根据目录跳转,如果觉得有用请点个赞哟,你的鼓励就是对我最大的支持,谢谢你。

字符串的输入和输出 

        c 库提供了许多读取字符串的函数:scanf()、gets()、fgets(),还有三个标注函数用于打印字符字符串:printf()、puts()、fputs(),它们都包含在头文件 stdio.h 。

scanf()

        格式控制字符串中使用格式控制说明%s,输入参数必须是字符型数组名。该函数从第一个空白字符作为字符串的开始,以下一个空白字符(空行、空格、制表符或换行符)作为字符串的结束,并自动将输入的数据和字符串结束符 '\o' 送入数组中。如果指定了字段宽度,如 %10s,那么scanf()将读取10个字符或读到空白字符(就是你的输入小于10字符时)停止。      

        还有一点就是scanf()也是有返回值的,该值等于scanf()成功读取的项数,就是你有几个字符串输入它就是返回几

#include<stdio.h >
int main(void)
{
	char name1[10],name2[10];
	int count;
	count=scanf("%s %s",name1,name2);   //scanf 返回值是成功读取的项数 
	printf("nice to meet you %s %s,count = %d \n",name1,name2,count);
	
	return 0;
}

printf()

        格式控制字符串中相应的格式控制说明用%s,输出参数可是是字符数组或字符串常量。输出遇到 ‘\0’ 结束,就是一次输出一个字符串嘛,字符串不都是以 ’\0‘ 结尾的。然后它也有返回值,printf()返回打印字符的个数,如输出错误则返回一个负值。我也不晓得怎么让它输出出错。(在写文件时常用,等我们过阵子写文件再看看)

返回值 6 ,那就是那个换行符 '\n' 也算一个,这就是它的返回值了,我们通常是很少用到的,知道它有返回值就好了。

gets()和 puts()

        gets()函数的参数是字符数组名。函数从输入得到一个字符串,遇到换行符输入结束,然后丢弃换行符,自动将输入的数据和 ‘\0’ 送入数组中。它是遇到回车结束所以 scanf()不能读的空白字符(空行、空格、制表符)它都可以读入

        puts()函数的参数可以是字符数组名或字符串常量。函数用于显示字符串,并在末尾添加换行符。注意,gets()丢弃输入中的换行符,但是 puts()在输出中添加换行符,所以我们使用时 puts()应与 gets()配对使用。

        然后还有就是它们也都有返回值,gets():输入成功返回值是字符串的第一个字符的地址,输入失败返回 NULL。puts():成功执行了输出字符串的操作返回非负数,否则返回 EOF。

#include<stdio.h >
int main(void)
{
	char s[10];
	int a;
	char *s2=gets(s);
	a=puts(s);    // puts()函数会在输出中添加换行符

    //s是该字符串的首地址,按照理论s2应该得到和s相同的地址,a则得到一个非负数 
	printf("s=%p s2=%p a=%d",s,s2,a); 
	return 0;
}

然后还要说一下 gets()的缺陷:我们知道 gets()只有一个唯一的参数字符数组名,就如上面这个代码而言,它唯一的参数是s,它只知道要将字符串写入 s 里,它却无法检查数组是否装得下输入行,如果输入的字符串过长超过 s 所能存储的话,会导致缓冲区溢出即多余的字符超出了指定的目标空间,如果这些多余的字符只是占用了尚未使用的内存,不会立刻出现问题,但是如果他们擦写掉程序中的其他数据,将会导致程序异常中止,或者还有其他情况。来我们把 s[ ] 数组长度改成 2 让它溢出一下看看

看,溢出啦!!!还好没出大事

小故事《不幸的 gets( ) 函数》:

        因为 gets()函数的不安全行为造成了安全隐患,过去有些人类通过系统编程利用 gets()做坏事,插入和运行一些破坏系统安全的代码。不久,c 编程社区的许多人都建议在编程时摒弃 gets()。制定 C99 标准的委员博采众议,把这些建议放入了标准,承认了 gets()的问题并建议不要再使用它。尽管如此,在标准中保留 gets()也合情合理,因为现有程序中含有大量使用该函数的代码,而且只要使用得当,它确实是一个很方便的函数。

        但是好景不长,随着C99委员会会长年老退休,安迪于##年##月##日接任会长一职,因为安迪至今单身,他非常不喜欢 99 这个名字,所以他将 C99 更名为 C11,由于这次更名完全出自安迪的一己私欲,让许多老委员寒了心,他们纷纷辞官回乡,他们一走空缺了大量职位,安迪为了委员会能正常运作,在c社区发布了招聘信息,没过多久就有大量的年轻人过来应聘,委员会经过这次大换血迎来了很多新鲜血液,安迪觉得是时候对委员会进行大改革了,随后他联合委员会对 C99 进行了更新制定新的 C11 标准 ,其中对于 gets()函数 C11 标准委员会采取了更强硬的态度,直接从标准中废除了 gets()函数。既然标准已经发布,那么编译器就必须根据标准来调整支持什么,不支持什么。然而在实际确是,许多编译器为了能兼容以前的代码,大部分却都背地里偷偷支持 gets()函数。

fgets()和fputs

        既然 gets()这么的危险,scanf()虽然能在 %s 转换说明中使用字段宽度(如 %5s)防止溢出,但它却无法读入空字符,更像是一种获取”单个单词“的函数而不是”获取字符串“,那么我们自然而然就衍生出它们的替代品 fgets()和 fputs()。

        fgets()函数有三个参数:第1个参数是字符数组名。第2个参数指明读入字符的最大数量,如果该参数是 n,那么 fgets()将读入 n-1 个字符,读入n-1 个字符是因为 fgets()会在输出行不溢出的情况下把换行符也加进字符串末尾。第3个参数指明要读入的文件,如果从键盘读入输入的数据,则以 stdin 作为参数,就是你要读键盘输入第二个参数就放 stdin(标准输入),等下看下代码我们就明白了,该函数其实是专门设计用于处理文件输入的。

        fputs()函数有两个参数:第1个参数是字符数组名。第2个参数指明要写入数据的文件,如要打印在显示器上,需用 stdout 作为该参数,就是我们要将字符串打印输出在显示器参数就用 stdout(标准输出)。fputs()函数不会在输出中添加换行符,所以还是那句话我们使用时 fputs()应与 fgets()配对使用。

我们来印证一下上面说的:

#include<stdio.h >
int main(void)
{
	char s[11];
	
	fgets(s,11,stdin);   //这里读入10个字符 
	
	printf("我们输入10个字符,fgets会存储下回车,所以下面fputs输出应该自带一个回车:\n");
	fputs(s,stdout);   //fputs函数输出时不会在输出中添加换行符
	
	printf("下面puts()函数会在输出中添加换行符,以下输出应该有两个回车 :\n");
	puts(s); //
	
	return 0;
}

        咦?怎么回事,我们的输入”helloworld“ 确实是10个字符呀,为什么第一个 fputs 并没有输出回车,而下面的 puts 只输出了一个回车,也就是说 fgets 并没有读到回车,这时候就需要我们好好回想一下 c 的字符串,我们说过c 中的字符串一定是以空字符 ’\0‘ 结束,所以“helloworld” 要占11个字节的位置,而我们上面说 fgets()函数会把回车加入字符串末尾是在输入行不溢出的前提下,所以我们的 fgest()读了 10 个字符,并把 helloworld \0 存储在数组 s 中。所以 fgets 第2个参数读入 n-1 字符这件事之外,我们自己还要注意字符串结尾的‘\0’。所以我们输入9个字符时就跟我们预期的一样了

好,我们算是把它搞明白了,当然它们两个也都有返回值:

        fgets()函数返回指向 char 的指针(地址),如果一切进行顺利,该函数返回的地址与传入的第1个参数相同,如果函数读到文件结尾,将返回一个特殊的指针:空指针(null pointer)。该指针保证不会指向有效的数据,所以可用于标识这种特殊情况 。fputs()函数返回值为;否则返回EFO(符号常量,其值为-1)。那我们知道就行了,就不一一论证了

字符串处理函数

        c 库提供了许多处理字符串的函数,ASCL C 把这些函数的原型放在 string.h 头文件中。其中最常用的函数有 strlen()、strcat()、strcmp()、strcpy().....等等等等

strlen()

        strlen( s1 )函数用于统计字符串的长度,参数 s1 可以是字符数组名或字符串常量,函数 strlen()返回字符串 ’\0‘ 之前的字符个数(不包括字符串末尾的 ‘\0’ )。就不敲了吧,写两个就算了,如:strlen( “hello” )返回值==5;char s=”world“    strlen( s )==5; 反正我们记住不带结尾的 ’\0‘ 就行。

strcat()和 strncat()

        strcat(s1,s2)函数有两字符串作为参数,参数 s1 必须是字符数组的基地址,参数 s2 可以是字符数组名或字符串常量。该函数把 s2 指向的字符串拷贝至 s1 指向的字符串末尾,s2 字符串的第1个字符将覆盖 s1 字符串末尾的空字符 ’\0‘,它的返回值是 s1 ,连接后 s2的值不变。

        但是strcat()函数有一个很大的缺陷就是它无法检查第1个数组是否装得下第2个字符串,所以更安全的版本是 strncat()函数

         strncat(s1,s2,n)函数用第3个参数指定最大的添加字符数,其他参数和 strcat()一样,它把 s2 字符串中的 n 个字符拷贝至 s1字符串末尾,,s2 字符串的第1个字符将覆盖 s1 字符串末尾的空字符 ’\0‘。不会拷贝 s2 字符串中的空字符 ‘\0’ 和其后的字符,然后在拷贝字符的末尾添加一个空字符,覆盖一个又增加一个,没变嘛。

#include<stdio.h>
#include<string.h>
int main(void)
{
	char s1[15]="hello w",s2[5]="orld" ;
	strcat(s1,s2);
	puts(s1);          //此时s1=“hello world” 
	
	strncat(s1,s2,2);  //此时用strncat把s2前两位连接到s1末尾,s1=”hello worldor “ 	 
	puts(s1);

	return 0;
}

strcmp()和 strncmp()

        strcmp(s1,s2)函数中的参数 s1 和 s2 可以是字符数组名或字符串常量。它返回一个整数,给出字符串 s1 和 s2 的比较结果:①若 s1 和 s2 相等,返回0 ②若 s1 大于 s2,返回一个正数 ③若 s1 小于 s2 返回一个负数。

        字符串的比较规则是:从两个字符串的首字符开始,以此比较相对应的字符的ASCLL码值,直到出现不同的字符或遇到 '\0' 为止。如果所有的字符都相同返回 0,否则以第一对不同的字符比较结果为准,返回这两数的差,就是第一个字符串的字符减去第二个字符串中的字符。

如我们比较 ”AAA“ 和 “ABA”,A 的ASCLL码值=65,B=66,按照理论应返回 65 - 66 = -1:

没毛病傲,确实是两数之差 -1。好吧,其实还是有一点毛病的,因为我还试了下 s1=“AAA”,s2=”AAC“,count 的值还是-1,在我编译器上它只有 0、-1、1三个返回值,好吧其实这个也不太重要,大多数情况下我们只在意该值是 0 还是非 0,就是它们是不是相等,负几还是正几都无关紧要了。

        然后我们再了解一下 strcmp()的进化体 strncmp(),strcmp()函数比较字符串中的字符,直到发现不同为止,这一过程可能会持续到字符串末尾。而 strncmp()函数在比较两个字符时,可以用它的第3个参数指定比较的字符数

        strncmp(s1,s2,n)函数参数 s1 和 s2 可以是字符数组名或字符串常量,该函数在比较 n 个字符后或遇到第一个空字符时停止比较。那我们比较一下 s1=”AAABBB“,s2=“AAACCC” 的前三个参数,它应该返回 0 :

strcpy( )和 strncpy()

        strcpy(char * s1,char * s2)函数参数 s1 必须是字符串数组基地址(就是 s1 这个指针应指向一个数据对象,如,数组),参数 s2 可是是字符数组名或字符串常量,该函数把 s2 指向的字符串(包括空字符 ’\0‘)拷贝至 s1 指向的位置,函数返回值是 s1,需要注意的是 s1 要有足够空间容纳 s2,且 s1 中的内容被覆盖。也就是 s1 必须 ≥ s2。

        当然 strcpy()和 strcat()有同样的问题,它不能检查目标空间 s1 是否能装的下 s2,那么我们自然而然也就衍生出 strcpy()的安全版本 strncpy()

        strncpy(s1,s2,n)函数同样把 s2 字符串中的字符拷贝至 s1 指向的位置,其拷贝的字符数不超过 n,函数返回值是 s1。如果 s2 中的字符小于 n,则拷贝整个字符串包括结尾空字符 ‘\0’,如果拷贝到第 n 个字符时还未拷贝完整个 s2,就不会拷贝空字符 ’\0‘。所以特别注意,拷贝的副本中不一定有空字符‘\0’,对此书上给出的解决方法是:把参数 n 设置为比目标数组大小少1,这样做确保存储的是一个字符串

#include<stdio.h>
#include<string.h>
int main(void)
{
	char s1[15]="hello",s2[15]="hello world" ;
	strcpy(s1,s2);  //此时经过拷贝s1值应为”hello world” 
	printf("strcpy(s1,s2)拷贝完后 s1 = %s \n\n",s1);
	
	strncpy(s1,"hello world! byebye!",15);              //s1只有15个字节大小,很显然它是拷贝不完的   
							                           //那我们拷15个字符按照理论它不会拷贝'\0'   
	printf("strncpy()拷贝完后 s1[14] = %c\n\n",s1[14]);         //第14这个个位置就应该是‘y’了  
	
	
	strncpy(s2,"hello world! byebye!",14);    //参数n设置为比s1小1就可以保证我们永远能得到一个字符串。 
	printf("strncpy()拷贝完后 s2 = %s\n\n",s2);  //此时s2应为 ”hello world! b” 
		 
	return 0;
}

strchr()、strrchr()和 strstr()

        strchr(char * s,char c):该函数如果 s 字符串包含 c字符,该函数返回指向 s 字符串首次出现 c 字符的指针,如果在字符串 s 中未找到 c 字符,则返回空指针。

        strrchr(char * s,char c):该函数返回 s 字符串中 s2 字符串中 c 字符最后一次出现的位置。如果未找到 c 字符,则返回空指针。

        strstr(char * s1,char * s2):该函数返回指向 s1 字符串中 s2 字符串出现的首位置。如果在 s1 中没有找到 s2,则返回空指针。

我们来全部论证一下:

#include<stdio.h>
#include<string.h>
int main(void)
{
	char s1[15]="hello world",s2[15]="world";
	
	
	char *RETURN=strchr(s1,'e');    //strchr()该函数应该返回指向 s1 字符串首次出现c字符的指针,那就是字符串s1中e的地址 
	printf("strchr:\n s1+1 = %p  RETURN = %p\n",s1+1,RETURN);   // s1+1就是第二个字节e的地址,那它们两个应该是一样的 
	
	
	
	RETURN=strrchr(s1,'l');      //strrchr()该函数返回 s1 字符串中 s2 字符串中 c 字符最后一次出现的位置
	printf("strrchr:\n s1+9 = %p  RETURN = %p\n",s1+9,RETURN);  //L最后一次出现的位置是第十个字节,那它们两个应该一样	


	
	RETURN=strstr(s1,s2);      //该函数返回指向 s1 字符串中 s2 字符串出现的首位置
	printf("strstr:\n s1+6 = %p  RETURN = %p\n",s1+6,RETURN);  //那就是w的位置咯, w在第七个字节的位置 
	
	return 0;
}

好的,下面完全一致,然后空字符我们就不一一返回来看了,你有空去试试吧

strpbrk()

        strpbrk( char * s1,char * s2):如果 s1 字符串中包含 s2 字符串中的任意字符,该函数返回指向 s1 字符串首位置的指针。如果在 s1 字符串中未找到任何 s2 字符串中的字符,则返回空字符。我们敲下代码看看

#include<stdio.h>
#include<string.h>
int main(void)
{
	char s1[15]="hello world",s2[15]="world",s3[15] = "mm";
	

	//如果 s1 字符串中包含 s2 字符串中的任意字符,该函数返回指向 s1 字符串首位置的指针
	char *RETURN=strpbrk(s1,s2);
	printf("s2 有字符包含在 s1 :s1 = %p  RETURN = %p\n",s1,RETURN); //我们知道 s1 就是”hello world”字符串的首地址 
	
	

	RETURN=strpbrk(s1,s3);  //应该返回 NULL
	printf("s3 中没有字符包含在 s1 :\n");
	if(RETURN){
		printf("bye bye!");   //此处应该不会输出 
	}
	
	
	return 0;
}

输出如下:

 还是有些出入的,我的 strpbrk()函数在 s2 有字符包含在 s1 中时,它的返回值是 s1+2 位置上 ‘l’ 的地址。只是我自己的哦。

  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值