4 长度受限的字符串函数
有的库函数在调用的时候,需要传入待处理的字符串的长度,因此称为长度受限的字符串函数。
这些函数提供了一种方便的机制,可以防止难以预料的长字符串从它们的目标数组溢出。
常见的有一下几个函数:
char *strncpy(char *dst, char const *src, size_t len); char *strncat(char *dst, char const *src, size_t len); int strncmp(char const *s1, char const *s2, size_t len);
举个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char message1[] = "hello "; char message2[] = "hello Beijing "; char message3[] = "Shanghai"; char message_all[] = "hello Beijing Shanghai"; if(strncmp(strncpy(strncat(message2, message3, strlen(message3)), message1, strlen(message1)), message_all,strlen(message_all)) == 0) printf("二者相等\n"); else printf("二者不相等\n"); system("pause"); return 0; }
打印输出:
这个例子举得并不十分恰当。因为长度都是按最大进行传入的,但也可以说明问题。
5 字符串查找基础
标准库中存在很多函数,它们用各种不同的方法查找字符串。这些各种各样的工具给了C程序员很大的灵活性。
5.1 查找一个字符串
在一个字符串中查找特定字符有两个库函数可用。
char *strchr(char const *str, int ch); char **strrchr(char const *str, int ch);
前者用来查找某字符第一次出现的位置(返回指向该地址的指针),后者用来查找某字符最后一次出现的位置(返回指向该地址的指针)。
这两个函数可以这样用:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char message1[] = "hello "; char message2[] = "hello Beijing "; char message3[] = "Shanghai"; char message_all[] = "hello Beijing Shanghai"; char *first_site, *last_site; first_site = strchr(message_all, 'h'); last_site = strrchr(message_all, 'h'); printf("字符串的长度是:%d\n",strlen(message_all)); printf("h第一次出现的位置是:%d\n", first_site - message_all); printf("h最后一次出现的位置是:%d\n", last_site - message_all); system("pause"); return 0; }
打印输出:
需要注意的是,该函数返回的并不是目标元素位置的值,而是指针,所以需要与该字符串的第一个元素指针作差,才可以得出结果。
注意:在查找的时候是区分大小写的。
5.2 查找任何几个字符
strpbrk
是一个更为常见的函数,用来查找某个字符串中任意字符第一次在目标字符串中出现的位置。它的原型如下:
char *strpbrk(char const *str, char const *group);
可以这样用:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char message_all[] = "hello Beijing Shanghai"; char *first_site; first_site = strpbrk(message_all, "abcde"); printf("字符串的长度是:%d\n",strlen(message_all)); printf("abcde第一次出现匹配字符的位置是:%d\n", first_site - message_all); system("pause"); return 0; }
打印输出:
容易看出,第一个匹配到的字符是e
,位置是1
。
5.3 查找一个子串
为了在字符串中查找一个子串,我们可以使用strstr函数,它的原型如下:
char *strstr(char const *s1, char const *s2);
举个实际使用中的例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char message_all[] = "hello Beijing Shanghai"; char *first_site; first_site = strstr(message_all, "Beijing"); printf("字符串的长度是:%d\n",strlen(message_all)); printf("Beijing第一次出现的位置是:%d\n", first_site - message_all); system("pause"); return 0; }
打印输出:
可以看到,在此次查找中,需要匹配到所有的字符,而不是某个或者局部。
6 高级字符串查找
接下来的一组函数简化了从一个字符串的起始位置中查找和抽取一个子串的过程。
6.1 查找一个字符串前缀
strspn
和strcspn
函数用于在字符串中的起始位置对字符串计数,它们的原型如下所示:
size_t strspn( char const *str, char const *group); size_t strcspn( char const *str, char const *group);
需要注意的是,这两个函数返回的 并不是元素指针,而是实际匹配字符的数量。
具体的使用方法可以来看个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int len1, len2; char buffer[] = "25,142,330,smith,J,239-4123"; len1 = strspn(buffer, "0123456789"); len2 = strcspn(buffer, ","); printf("0123456789的起始匹配数是:%d\n", len1); printf(",的起始不匹配数是:%d\n", len2); system("pause"); return 0; }
打印输出:
从上面例子中可以看出,strspn
函数是从头开始找符合所找字符串的字符,直到找不到为止。在该例中,,
已经不合适了,所以连续查找的情况下,合适的只有2
个。
strcspn函数却恰好相反,找的是不符合的,开头的2和5显然都不符合,,
是符合的,所以连续查找的情况下,合适的有2
个。
6.2 查找标记
一个字符串常常包含好几个单独的部分,他们彼此被分隔开。每次为了处理这些部分,首先必须把它们从字符串中抽取出来。
strtok函数就可以实现这样的功能。它从字符串中隔离各个单独的称为标记的部分。并丢弃分隔符。它的原型如下:
char *strtok( char *str, char const *sep);
注意:
- 当strtok函数执行任务时,它会修改它所处理的字符串。如果源字符串不能被修改,那就复制一份,将这份拷贝传递给strtok函数。
- 如果strtok函数的第1个参数不是NULL,函数将找到字符串的第1个标记。strtok同时将保存它在字符串中的位置。如果strtok函数的第1个参数是NULL,函数就在同一个字符串中从这个被保存的位置开始像前面一样查找下一个标记。
举个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int add = 0; char buffer[] = "25,142,330,smith,J,239-4123"; char *token = NULL; for (token = strtok(buffer, ","); token != NULL; token = strtok(NULL, ",")) { printf("%s\n", token); add++; } printf("--------------------------\n"); printf("add的值为:%d\n",add); system("pause"); return 0; }
打印输出:
从上面的例子中可以看出,以我们需要寻找的标记为分界,每循环一次,得到一个分割的子串,直到全部分割完毕。共分割6
次。
7 错误信息
C语言的库函数在执行失败时,都会有一个错误码(0 1 2 3 4 5 6 7 8 9 …),操作系统是通过设置一个外部的整型变量errno
进行错误代码报告的。也就是说,一个错误码,对应了一种错误类型,strerror
函数把其中一个错误代码作为参数并返回一个指向用于描述错误的字符串的指针。这个函数的原型如下:
char *strerror(int error_number);
举个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { for(int i = 0; i < 10; i++) printf("%s\n",strerror(i)); system("pause"); return 0; }
打印输出:
可以看到,不同的操作码,对应着不同的错误类型,而错误码0
表示没有错误。其他均表示各种各样的错误。这部分内容了解即可。不需要掌握。也不需要知道每个操作码究竟代表哪种错误。
8 字符操作
标准库包含了两组函数,用于操作单独的字符,它们的原型位于头文件ctype.h。第1组函数用于对字符串分类,而第2组函数用于字符转换。
8.1 字符分类
每个分类函数接受一个包含字符值的整形参数。函数测试这个字符并返回一个整形值,表示真或假。下面的表格列出了每个函数以及返回真所需要的条件。
函数 | 返回真所需要的条件 |
iscntrl | 控制字符 |
isspace | 空白字符:空格,换页’\f’,换行’\n’,回车’\r’,制表符’t’或垂直制表符’\v’ |
isdigit | 十进制数字 |
isxdigit | 十六进制数字,包含大小写形式的a~f |
islower | 小写字母 |
isupper | 大写字母 |
isalpha | 字母(大小写皆可) |
isalnum | 字母或数字 |
ispunct | 任何不属于数字或字母的图形字符(可打印符号) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
所以这些函数是用来判定字符串元素的, 举个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> int main() { char temp[] = "To carry things with great virtue"; for (int i = 0; i < strlen(temp); i++) { if (islower(temp[i])) printf("temp[%d] : %c是小写字母\n", i, temp[i]); else if (isupper(temp[i])) printf("temp[%d] : %c是大写字母\n", i, temp[i]); else if(isspace(temp[i])) printf("temp[%d] : %c是空格\n", i, temp[i]); } printf("\n"); system("pause"); return 0; }
打印输出:
可以看到,此时已经将temp
每个元素究竟是大写字母还是小写字母,或者是空格进行了判定。