🍍字符串函数
2021-1-23
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property
 

 
C库提供了多个处理字符串的函数,ANSI C把这些函数的原型放在string.h头文件中。常用的函数有strlen()strcat()strcmp()strncmp()strcpy()strncpy()。另外,还有sprintf()函数,其原型在stdio.h头文件中
 

strlen()

strlen()函数用于统计字符串的长度
 
fit()函数把第39个元素的逗号替换成'\0'字符。puts()函数在空字符处停止输出,并忽略其余字符。然而,这些字符还在缓冲区中,下面的函数调用把这些字符打印了出来:
notion image
 

strcat()

strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char *(即,指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。
从以上输出可以看出,flower改变了,而addon保持不变
 

strncat()

strcat()函数无法检查第1个数组是否能容纳第2个字符串。如果分配给第1个数组的空间不够大,多出来的字符溢出到相邻存储单元时就会出问题。当然,可以用strlen()查看第1个数组的长度。注意,要给拼接后的字符串长度加1才够空间存放末尾的空字符。或者,用strncat()
strncat()函数的第3个参数指定了最大添加字符数。例如,strncat(bugs,addon, 13)将把 addon字符串的内容附加给bugs,在加到第13个字符或遇到空字符时停止。因此,算上空字符(无论哪种情况都要添加空字符),bugs数组应该足够大,以容纳原始字符串(不包含空字符)、添加原始字符串在后面的13个字符和末尾的空字符。
 
 

strcmp()

假设要把用户的响应与已储存的字符串作比较
这个程序看上去没问题,但是运行后却不对劲。ANSWER和try都是指针,所以try != ANSWER检查的不是两个字符串是否相等,而是这两个字符串的地址是否相同。因为ANSWE和try储存在不同的位置,所以这两个地址不可能相同,因此,无论用户输入什么,程序都提示输入不正确。
该函数要比较的是字符串的内容,不是字符串的地址。可以自己设计一个函数,也可以使用C标准库中的strcmp()函数(用于字符串比较)。该函数通过比较运算符来比较字符串,就像比较数字一样。如果两个字符串参数相同,该函数就返回0,否则返回非零值。
由于非零值都为“真”,所以许多经验丰富的C程序员会把该例main()中的while循环头写成:
strcmp()函数比较的是字符串,不是整个数组,这是非常好的功能。虽然数组try占用了40字节,而储存在其中的"Grant"只占用了6字节(还有一个用来放空字符),strcmp()函数只会比较try中第1个空字符前面的部分。所以,可以用strcmp()比较储存在不同大小数组中的字符串。如果用户输入GRANT、grant或Ulysses S.Grant会怎样?程序会告知用户输入错误。希望程序更友好,必须把所有正确答案的可能性包含其中。这里可以使用一些小技巧。例如,可以使用#define定义类似GRANT这样的答案,并编写一个函数把输入的内容都转换成小写,就解决了大小写的问题。但是,还要考虑一些其他错误的形式。
 
strcmp()的返回值
注意strcmp()函数比较的是字符串,不是字符,所以其参数应该是字符串(如"apples"和"A"),而不是字符(如'A')
 
ASCII标准规定,在字母表中,如果第1个字符串在第2个字符串前面,strcmp()返回一个负数;如果两个字符串相同,strcmp()返回0;如果第1个字符串在第2个字符串后面,strcmp()返回正数。然而,返回的具体值取决于实现。
如果两个字符串开始的几个字符都相同会怎样?一般而言,strcmp()会依次比较每个字符,直到发现第 1 对不同的字符为止。然后,返回相应的值。
 
strcmp()函数检查程序是否要停止读取输入
该程序在读到EOF字符(这种情况下s_gets()返回NULL)、用户输入quit或输入项达到LIM时退出。 顺带一提,有时输入空行(即,只按下Enter键或Return键)表示结束输入更方便。为实现这一功能,只需修改一下while循环的条件即可:
这里,input[ct]是刚输入的字符串,input[ct][0]是该字符串的第1个字符。如果用户输入空行, s_gets()便会把该行第1个字符(换行符)替换成空字符。所以,下面的表达式用于检测空行:
 
 

strncmp()

strcmp()函数比较字符串中的字符,直到发现不同的字符为止,这一过程可能会持续到字符串的末尾。而strncmp()函数在比较两个字符串时,可以比较到字符不同的地方,也可以只比较第3个参数指定的字符数。例如,要查找以"astro"开头的字符串,可以限定函数只查找这5 个字符
 
 

strcpy()和strncpy()

如果pts1和pts2都是指向字符串的指针,那么下面语句拷贝的是字符串的地址而不是字符串本身:
如果希望拷贝整个字符串,要使用strcpy()函数。
 
下面程序要求用户输入以q开头的单词。该程序把输入拷贝至一个临时数组中,如果第1 个字母是q,程序调用strcpy()把整个字符串从临时数组拷贝至目标数组中。strcpy()函数相当于字符串赋值运算符。
notion image
 
注意,只有在输入以q开头的单词后才会递增计数器i,而且该程序通过比较字符进行判断:
这行代码的意思是:temp中的第1个字符是否是q?当然,也可以通过比较字符串进行判断:
 
请注意,strcpy()第2个参数(temp)指向的字符串被拷贝至第1个参数(qword[i])指向的数组中。拷贝出来的字符串被称为目标字符串,最初的字符串被称为源字符串。参考赋值表达式语句,很容易记住strcpy()参数的顺序,即第1个是目标字符串,第2个是源字符串。
 
strcpy()的其他属性
strcpy()函数还有两个有用的属性。第一,strcpy()的返回类型是 char *,该函数返回的是第 1个参数的值,即一个字符的地址。第二,第 1 个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分。
notion image
注意,strcpy()把源字符串中的空字符也拷贝在内。在该例中,空字符覆盖了copy数组中that的第1个t。注意,由于第1个参数是copy +7,所以ps指向copy中的第8个元素(下标为7)。因此puts(ps)从该处开始打印字符串。
notion image
 
更谨慎的选择:strncpy()
strcpy()和 strcat()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。拷贝字符串用 strncpy()更安全,该函数的第 3 个参数指明可拷贝的最大字符数`
notion image
strncpy(target, source, n)把source中的n个字符或空字符之前的字符(先满足哪个条件就拷贝到何处)拷贝至target中。因此,如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是,strncpy()拷贝字符串的长度不会超过n,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝的副本中不一定有空字符。鉴于此,该程序把 n 设置为比目标数组大小少1(TARGSIZE-1),然后把数组最后一个元素设置为空字符:
这样做确保储存的是一个字符串。如果目标空间能容纳源字符串的副本,那么从源字符串拷贝的空字符便是该副本的结尾;如果目标空间装不下副本,则把副本最后一个元素设置为空字符。
 
 

sprintf()

sprintf()函数声明在stdio.h中,而不是在string.h中。该函数和printf()类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf()的第1个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表。
notion image
sprintf()函数获取输入,并将其格式化为标准形式,然后把格式化后的字符串储存在formal中。
 

其他字符串函数

ANSI C库有20多个用于处理字符串的函数,下面总结了一些常用的函数。
该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1。
 
该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符。
 
该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。该函数返回s1。
 
该函数把s2字符串中的n个字符拷贝至s1字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1。
 
如果s1字符串在机器排序序列中位于s2字符串的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数。
 
该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符后或遇到第1个空字符时停止比较。
 
如果s字符串中包含c字符,该函数返回指向s字符串首位置的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。
 
如果 s1 字符中包含 s2 字符串中的任意字符,该函数返回指向 s1 字符串首位置的指针;如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。
 
该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。
 
该函数返回指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针。
 
该函数返回s字符串中的字符数,不包括末尾的空字符。
 
请注意,那些使用const关键字的函数原型表明,函数不会更改字符串。例如,下面的函数原型:
表明不能更改s2指向的字符串,至少不能在strcpy()函数中更改。但是可以更改s1指向的字符串。这样做很合理,因为s1是目标字符串,要改变,而s2是源字符串,不能更改。
 
关键字restrict限制了函数参数的用法。例如,不能把字符串拷贝给本身。 size_t类型是sizeof运算符返回的类型。C规定sizeof运算符返回一个整数类型,但是并未指定是哪种整数类型,所以size_t在一个系统中可以是unsigned int,而在另一个系统中可以是 unsigned long。string.h头文件针对特定系统定义了 size_t,或者参考其他有 size_t定义的头文件。
 
 

字符串排序

处理一个按字母表顺序排序字符串的实际问题。准备名单表、创建索引和许多其他情况下都会用到字符串排序。该程序主要是用 strcmp()函数来确定两个字符串的顺序。一般的做法是读取字符串函数、排序字符串并打印出来。
notion image
该程序的巧妙之处在于排序的是指向字符串的指针,而不是字符串本身。
最初,ptrst[0]被设置为input[0],ptrst[1]被设置为input[1],以此类推。这意味着指针ptrst[i]指向数组input[i]的首字符。每个input[i]都是一个内含81个元素的数组,每个ptrst[i]都是一个单独的变量。排序过程把ptrst重新排列,并未改变input。例如,如果按字母顺序input[1]在intput[0]前面,程序便交换指向它们的指针(即ptrst[0]指向input[1]的开始,而ptrst[1]指向input[0]的开始)。这样做比用strcpy()交换两个input字符串的内容简单得多,而且还保留了input数组中的原始顺序。
notion image
 
我们采用选择排序算法(selection sort algorithm)来排序指针。
具体做法是,利用for循环依次把每个元素与首元素比较。如果待比较的元素在当前首元素的前面,则交换两者。循环结束时,首元素包含的指针指向机器排序序列最靠前的字符串。然后外层for循环重复这一过程,这次从input的第2个元素开始。当内层循环执行完毕时,ptrst中的第2个元素指向排在第2的字符串。这一过程持续到所有元素都已排序完毕。
进一步分析选择排序的过程。下面是排序过程的伪代码:
找出剩余元素中的最大值,并将其放在第n个元素中 具体过程如下。首先,从n = 0开始,遍历整个数组找出最大值元素,那该元素与第1个元素交换;然后设置n = 1,遍历除第1个元素以外的其他元素,在其余元素中找出最大值元素,把该元素与第2个元素交换;重复这一过程直至倒数第 2 个元素为止。现在只剩下两个元素。比较这两个元素,把较大者放在倒数第2的位置。这样,数组中的最小元素就在最后的位置上。 这看起来用for循环就能完成任务,但是还要更详细地分析“查找和放置”的过程。在剩余项中查找最大值的方法是,比较数组剩余元素的第1个元素和第2个元素。如果第2个元素比第1个元素大,交换两者。现在比较数组剩余元素的第1个元素和第3个元素,如果第3个元素比较大,交换两者。每次交换都把较大的元素移至顶部。继续这一过程直到比较第 1 个元素和最后一个元素。比较完毕后,最大值元素现在是剩余数组的首元素。已经排出了该数组的首元素,但是其他元素还是一团糟。下面是排序过程的伪代码:
比较第n个元素与第1个元素,如果第n个元素更大,交换这两个元素的值 看上去用一个for循环也能搞定。只不过要把它嵌套在刚才的for循环中。外层循环指明正在处理数组的哪一个元素,内层循环找出应储存在该元素的值。把这两部分伪代码结合起来,翻译成 C代码,就得到了程序清单stsrt()函数。
C库中有一个更高级的排序函数:qsort()。该函数使用一个指向函数的指针进行排序比较
 
 

ctype.h字符函数

ctype.h系列里有与字符相关的函数。虽然这些函数不能处理整个字符串,但是可以处理字符串中的字符。例如ToUpper()函数,利用toupper()函数处理字符串中的每个字符,把整个字符串转换成大写;定义的 PunctCount()函数,利用ispunct()统计字符串中的标点符号个数。另外,该程序使用strchr()处理fgets()读入字符串的换行符(如果有的话)。
notion image
该程序使用fgets()strchr()组合,读取一行输入并把换行符替换成空字符。这与使用s_gets()的区别是:s_gets()会处理输入行剩余字符(如果有的话),为下一次输入做好准备。而本例只有一条输入语句,就没必要进行多余的步骤。
 
 
 
ToUpper()函数利用toupper()处理字符串中的每个字符(由于C区分大小写,所以这是两个不同的函数名)。根据ANSI C中的定义,toupper()函数只改变小写字符。但是一些很旧的C实现不会自动检查大小写,所以以前的代码通常会这样写:
顺带一提,ctype.h中的函数通常作为宏(macro)来实现。这些C预处理器宏的作用很像函数,但是两者有一些重要的区别。
 
 

把字符串转换为数字

数字既能以字符串形式储存,也能以数值形式储存。把数字储存为字符串就是储存数字字符。例如,数字213以'2'、'1'、'3'、'\0'的形式被储存在字符串数组中。以数值形式储存213,储存的是int类型的值。
C要求用数值形式进行数值运算。但是在屏幕上显示数字则要求字符串形式,因为屏幕显示的是字符。printf()和 sprintf()函数,通过%d 和其他转换说明,把数字从数值形式转换为字符串形式,scanf()可以把输入字符串转换为数值形式。C 还有一些函数专门用于把字符串形式转换成数值形式。
假设你编写的程序需要使用数值命令形参,但是命令形参数被读取为字符串。因此,要使用数值必须先把字符串转换为数字。

atio()

如果需要整数,可以使用atoi()函数(用于把字母数字转换成整数),该函数接受一个字符串作为参数,返回相应的整数值。
notion image
命令行参数3被储存为字符串3\0。atoi()函数把该字符串转换为整数值3,然后该值被赋给times。该值确定了执行for循环的次数。
如果运行该程序时没有提供命令行参数,那么argc < 2为真,程序给出一条提示信息后结束。如果times 为 0 或负数,情况也是如此。C 语言逻辑运算符的求值顺序保证了如果 argc < 2,就不会对atoi(argv[1])求值。
 
如果字符串仅以整数开头,atio()函数也能处理,它只把开头的整数转换为字符。例如, atoi("42regular")将返回整数42。如果在命令行输入hello what会怎样?在我们所用的C实现中,如果命令行参数不是数字,atoi()函数返回0。然而C标准规定,这种情况下的行为是未定义的。因此,使用有错误检测功能的strtol()函数会更安全。
 
该程序中包含了stdlib.h头文件,因为从ANSI C开始,该头文件中包含了atoi()函数的原型。除此之外,还包含了atof()atol()函数的原型。atof()函数把字符串转换成 double 类型的值, atol()函数把字符串转换成long类型的值。atof()atol()的工作原理和atoi()类似,因此它们分别返回double类型和long类型。
ANSI C还提供一套更智能的函数:strtol()把字符串转换成long类型的值,strtoul()把字符串转换成unsigned long类型的值,strtod()把字符串转换成double类型的值。这些函数的智能之处在于识别和报告字符串中的首字符是否是数字。而且,strtol()strtoul()还可以指定数字的进制。
 

strtol()

nptr是指向待转换字符串的指针,endptr是一个指针的地址,该指针被设置为标识输入数字结束字符的地址,base表示以什么进制写入数字
notion image
当base分别为10和16时,字符串"10"分别被转换成数字10和16。
还要注意,如果end指向一个字符,*end就是一个字符。因此,第1次转换在读到空字符时结束,此时end指向空字符。打印end会显示一个空字符串,以%d转换说明输出*end显示的是空字符的ASCII码。
对于第2个输入的字符串,当base为10时,end的值是'a'字符的地址。所以打印end显示的是字符串"atom",打印*end显示的是'a'字符的ASCII码。然而,当base为16时,'a'字符被识别为一个有效的十六进制数,strtol()函数把十六进制数10a转换成十进制数266。 strtol()函数最多可以转换三十六进制,'a'~'z'字符都可用作数字。
strtoul()函数与该函数类似,但它把字符串转换成无符号值。strtod()函数只以十进制转换,因此它值需要两个参数。 许多实现使用itoa()ftoa()函数分别把整数和浮点数转换成字符串。但是这两个函数并不是C标准库的成员,可以用sprintf()函数代替它们,因为sprintf()的兼容性更好。
  • C
  • 字符串输入输出命令行参数
    目录