字符和字符串函数——C Prime Plus ch11

本文详细介绍了C语言中字符串的定义、创建方法,包括字符串字面量、字符串数组和初始化、指针表示法。探讨了字符串输入/输出函数,如gets、puts、fgets、fputs,强调了安全性及适用场景。还讨论了自定义输入/输出函数的可能性,并介绍了字符串函数如strlen、strcpy等,以及字符串在排序、ctype.h字符函数、命令行参数和转换为数字的应用。
摘要由CSDN通过智能技术生成

一、字符串的定义

字符串是以空字符‘\0’结尾的char类型数组。定义字符串的方式有三种。

1.字符串字面量(字符串常量)

用双引号括起来的内容称为字符串字面量,也称作字符串常量。如果字符串常量之间用空白字符分隔(或者没有分隔),C会将其视为串联起来字符串常量。

字符串常量属于静态存储类别,在整个程序的生命期内存在。用双引号括起来的内容被视为指向该字符串存储位置的指针,指针指向的内容是字符串的首字符。

2.字符串数组和初始化

① const char m1[40] = " Limit yourself to onr line's worth ";

②const char m2[40]={ 'L','I','M','\0'};

③const char m3[ ]=" Limit yourself to onr line's worth ";

注意: 字符串数组初始化可以直接用双引号括起来的字符串常量,如果要一个个初始化,一定要自己在数组最后加上空字符,数组大小可以自己设置(一定要预留空字符的大小),也可以让编译器自己确定(只能用在创建并初始化的情况,否则不能用),声明数组时,数组大小必须是可求值的整数。

3.指针表示法创建数组

const char *pt1 = " Limit yourself to onr line's worth ";

注意: pt1 和 m1都是该字符串的地址,但是这两种形式并不完全相同 。

带双引号的字符串已经在系统中留出了存储空间(静态存储),字符串数组在创建好了之后,程序在运行时才会为数组分配空间,并且把静态区存储的字符串拷贝一份过去(现在有两个字符串,一个是字符串常量里的,一个是字符串数组里的,存储的内容一样),并且数组名是个常量,不能改变其值,这意味着你改变数组里的值,原字符串不会受到影响。

指针形式开始执行程序,系统只会给指针留出一个存储空间(int型),并且把字符串的地址存储在指针变量中(相同字符串只有一份),指针的值可以改变,如果通过指针改变了字符串,那么整个字符串全都变了,指针元素所指向的字符串不必存储在连续的内存中。

因此,如果打算修改字符串或者为字符串输入预留空间,就用字符串数组,否则会出现未定义的情况(内存访问错误等);

如果字符串保持不变,用指向const指针更有效率。

事实上,字符串的大部分操作需要用到指针,比如拷贝字符串,对于字符串数组,除了用到函数外,不然就要一项项进行拷贝,指针则可以直接赋值,让两者指向同一个位置,效率更高。

二、字符串输入/输出函数

1.字符串输入要做的第一件事就是分配空间,最简单的方法就是在声明时显式子指明数组的大小。

为什么不推荐用指针,指针如果没初始化,会指向任何地方,这是非常严重的错误。

2.为字符串分配好内存后,便可以通过读取字符串的函数来读取字符串:

①gets()函数/puts()函数------------------不推荐

gets()函数读取整行输入,直到遇到换行符,然后丢弃换行符,存储其余字符,并在字符末尾添加上空字符,最终读取字符串到指定数组中;

不足:

无法检查数组是否装得下输入行,如果过长会导致缓冲区溢出(会访问未分配内存,导致擦除其他数据等问题),这带来了安全问题,现在几乎不再使用。

puts()函数:

参数:字符串的地址传入,该函数在显示字符串时会自动在其末尾添加一个换行符(gets会舍弃换行符),函数遇到空字符会停止(如果没有空字符,会一直乱输入存储中的数据,直到遇到空字符,不要这样做)

 ②fgets()函数/fputs()函数

fgets()和fputs()用于处理文件的输入输出——函数有三个参数,第一个参数是字符串要存储的数组,第二个参数是指明了读入字符的最大数量(包括空字符)或者遇到读到的第一个换行符结束,第三个参数指明要读入的文件,如果输入从键盘输入的数据,则以stdin作为参数。

与gets()不同点:gets()会舍弃换行符,fgets()会保留换行符。

fgets()函数返回char的指针,如果一切顺利,函数返回与传入的第一个参数相同(字符串第一个字符的地址),如果到文件结尾,返回空指针,在代码中,可以用0或者NULL代替。

fputs()函数

函数有两个参数,第一个参数是你要输出字符串的数组名,第二个参数指明要写入的文件,如果在计算器显示器上显示,stdout作为参数。该函数不同于puts()函数,不会在末尾加换行符。

这一对函数优缺点:

优点:处理过长输入几乎没问题,可以根据是否有换行符来判断是否读取了一行。

 第一次读入9个字符后,会继续循环读入,循环输出。

系统使用缓冲IO,这意味着用户在按下enter键之前,所有的输入被存储在缓冲区中,按下enter键在输入中加入了一个换行符,并把整行输入发送给fgets()函数,因此可以根据是否有换行符来判断是否读取了一行。

坏处:不想要换行符怎么办?

① 在以存储的字符串里查找,将其替换为空字符;

 ②仍有字符串在缓冲区怎么办? 丢弃掉

 ③综合起来,碰到换行就变为空字符,并把换行符后的字符全丢掉

③scanf()函数/printf()函数

scanf()函数从第一个非空白字符读取,直到遇到空白字符(空行、空格、换行、制表)停止,更像是读取一个单词的函数,可以指定字段宽度来限制读取的字符个数(防止溢出),scanf()函数会返回独到的项数(1个字符串或者2个字符串,而不是字符的数量),读到文件结尾会返回EOF.

printf()函数以字符串地址作为参数,不会自动在每个字符串末尾加换行符需要手动加,优点在于很方便打印多个字符串。

综上所述:

对于输入:输入过长,使用fgets()函数,并且对于超出的字符,可选择不同的处理方式;

                 如果目标存储区装得下输入行,gets()和fgets()均可;

                如果输入包含工具名,库存量,单价等混合数据,scanf()最方便(通过字宽限制溢出)。

对于输出:puts()自动加上换行符;

                  fputs()不会加换行符;

                printf() 对于多个字符串输出最方便。 printf("%s,%s,%s.",name,name2,name3)

三、自定义输入/输出函数

不一定非要使用C库中的标准函数,可以自己定义输入输出函数。

s_gets()函数——读取整行输入并用空字符代替换行符,或者读取一部分输入,丢弃其他部分

测试条件:

while(string[i] !=0)

putchar(string[i++]);

还可以这样写 while(*string)

 putchar(string[i++]);

参数 const char * string 和const char string[]等价,但要处理字符串,实际参数可以为数组名、字符串常量、指针等,声明为第一种情况可以提醒用户,实际参数不一定是数组。

四、字符串函数

均要使用string.h头文件

①strlen() ——输出字符串长度(不包括空字符);

②stract(arr,arr2)——第二个字符接到第一个字符后边,返回第一个参数;

缺点:无法检查第一个字符是否能容纳下添加进来新字符的长度,可能会出现问题。

③strnact(arr,arr2,n)——添加到第n个字符或空字符结束(n不包括空字符,一定要为空字符空出位置)

④strcmp(arr1,arr2)会依次比较每个字符,直到发现第一对不同的字符为止,然后返回相应的值。如果两个字符串相同,返回0,如果arr1的字符ascii码在arr2字符的后边,返回正数,反之,返回负数。(大写字母在小写字母前边)

注意:strcmp比较的是字符串,不要往里传字符。

⑤strncmp(arr1,arr2,n)限定函数只查找前n个字符的比较情况。

⑥strcpy(arr1,arr2)——把arr2数组中的字符串拷贝到arr1中,必须要保证1中有足够的空间。

该函数返回的是第一个参数的值(一个字符的地址),并且第一个参数不必指向数组的开始。

⑦strncmp(arr1,arr2,n)——第三个参数指明拷贝的最大字符数(不包括空字符,提前预留)

⑧sprintf(arr,"  ")——该函数是把输入写入数组,而不是打印到屏幕上,后边参数同printf(),该函数同样可以把多元素多格式组合成一个字符串。

五、字符串示例:字符串排序

处理一个字母表排序字符串问题

#include<stdio.h>
#include<string.h>
char* s_gets(char* p, int n);
void str_sort(char* p[], int n);
int main(void)
{
	char input[20][81];
	char* pt[20];
	int i = 0;
	int k = 0;

	puts("Input up to 20;ines,and i will sort them.");
	puts("To stop,press the Enter key at a line's start.");
	while (i < 20 && s_gets(input[i], 81)!= NULL && input[i][0] != '\0')
	{
		pt[i] = input[i];
		i++;
	}
	str_sort(pt, i);
	puts("Here is the sorted list:");
	for (k = 0; k < i; k++)
		puts(pt[k]);
	return 0;
}
void str_sort(char* p[], int n)
{
	char* temp;
	int i = 0;
	int j = 0;

	for (i = 0; i < n-1; i++)
	{
		for (j = i+1; j < n; j++)
		{
			if (strcmp(p[i], p[j]) > 0)
			{
				temp = p[i];
				p[i] = p[j];
				p[j] = p[i];
			}
		}
	}
}
char* s_gets(char* p, int n)
{
	char* ret_val;
	int i = 0;

	ret_val = fgets(p , n, stdin);
	if(ret_val )
	{
		while(p[i] != '\n' && p[i] != '\0')
			i++;
		if (p[i] == '\n')
			p[i] = '\0';
		else
		{
			while (getchar() != '\n')
				continue;
		}
	}
	return ret_val;
}

分析: 第一步,存储输入,并用指针指向每一行输入;需要定义一个二维数组,一个指针数组,什么情况下赋值?(小于20,并且读入不为空,首字符不为空)

第二步:数组比较——调用函数——冒泡排序(i从0开始,j从i+1开始比较,交换)

第三步:输出排序好的数组——循环(用指针输出,因为是指针位置变化了,数组原内容不变)——s_gets()函数——fgets()进行输入,保证顺利进行,if判断,同时需要一个指针指向fgets(),while循环到换行符或零字符进行不同情况判断。

为什么选择排序指针?——方便,只需要指针进行赋值,不然数组赋值还需要用到strcpy函数。

六、ctype.h字符函数和字符串

第七章介绍的ctype.h系列函数,这些函数不能处理字符串,但是可以处理字符串中的字符。

以下程序分别把字符串转换为大写,统计标点符号个数。

 

直接判断字符更简单;字符判断除了s_get()

也可以通过strchr寻找'\n',如果地址不是空,将其替换为’\0‘;和s_get()区别在于后边多余的字符s_get处理了,寻找这个没处理(在缓冲区,会影响下一次输入),因为本程序只有一句输入,所以不需要处理。 

七、命令行参数

C编译器允许main没有参数或者有两个参数,当有两个参数时——命令行里,第一个参数为命令行中的字符串数量(包括程序名字,如上边的test_11),第二个参数是每个字符串地址存在第二个参数数组里,程序名一般在argv[0]内。

在命令行中,程序参数就直接在名字后边写,用空格隔开。

在许多环境里,允许用双引号括起来形成一个参数。 

八、字符串转换为数字

在VS2022环境里,我们可以通过printf()/fprintf()/scanf()实现字符串和数字数值的转换,对于命令行中,如何将字符串改为数字呢?——atoi()函数

atoi()函数介绍:

()里输入字符串(数字字符串/开头是数字的字符串),返回里边的数字,如果参数不是数字,函数立马返回0.

还有其他函数: atol();atof(); 

除此以外,在除过命令行系统中还有更智能的函数——可以识别和报告字符串中的首字符是否是数字;

 strtol()函数——str->long,可以指定数字的进制;

strtoul()函数——str->unsigned long,可以指定数字的进制;

strtod()函数——str->double.

函数原型:long  strtol(const char * restrict nptr,char ** restrict endptr,int base);

nptr是指带转换字符串的指针,endptr是一个指针地址,被设置为表示输入数字结束字符的地址,base表示以什么进制写入数字(最多到36进制)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值