C语言之字符串操作
C 语言提供了丰富的字符串处理相关的库函数,这些函数基本上,都声明在头文件string.h当中,所以使用它们需要包含这个头文件。这里只介绍几种最基本的和最常用的,以及自己实现这些函数的方式。
字符串长度strlen
strlen
函数全名:string_length
函数声明:
size_t strlen (const char *s);
函数作用:返回当前字符串的长度,也就是字符数组中空字符’\0’前面的字符数量。==不包括空字符’\0’!==
1 | int len; |
手动实现my_strlen
1 | size_t my_strlen1(const char* str) { |
字符串复制strcpy,strncpy
strcpy
函数全名: string_copy
函数声明:
char *strcpy(char *dest, const char *src);
函数作用: 将 src 中存储的以空字符 ‘\0’ 结束的字符串复制到 dest所指向的数组中。
也就是说,会从首字符开始逐个字符地从 src 复制字符,包括空字符在内,都会从dest首位置开始,复制到dest当中。
这个过程中,src数组是不会被修改的,所以它被const修饰。总之,该函数调用后,dest 将包含 src 的完整副本。
1 | char src[] = "hello"; |
函数返回值:
该函数会返回指向目标数组 dest 的指针,一般来说,该函数的返回值没有什么意义,推荐忽略它。
但某些场景下,这个返回值允许strcpy 函数的调用,可以被嵌套使用或者用于更复杂的表达式中。比如:
1 | // strcpy返回复制完成后指向 dest 数组的指针 |
注意:
- strcpy函数是不安全的,它并不会检查dest是否真的能够包含src数组。如果dest不够大,就会因数组越界而产生未定义行为。
- 为了安全起见,可以考虑使用strncpy函数解决这一问题,虽然它可能效率更低一些。
strncpy
函数声明:
char *strncpy(char *dest, const char *src, size_t n);
函数作用:
该函数会将最多n个字符从src中复制到dest数组中:
- 如果
n < strlen(src) + 1
,也就是 n 小于源字符串的长度 + 1时,此时strncpy函数就无法将整个源字符串数据都复制到 dest 中,并且此时复制完成的 dest 数组也不会以空字符结尾,无法表示一个字符串。不过好在,它不会引起越界问题,是安全的操作。 - 如果
n = strlen(src) + 1
,即 n 恰好是源字符串长度 + 1时,strncpy函数会将src完整复制到dest数组中,包括空字符。 - 如果
n > strlen(src) + 1
,即 n 完全大于源字符串长度 + 1时,strncpy函数不仅会完整复制src字符串到dest中,还会将剩余 (n - strlen(src) - 1) 个字符设置为空字符。
strncpy函数一定会处理n个字符数据,如果n比较大,在复制完源数组后,它会顺道将 dest 数组中的元素置为空字符。
实际开发中,建议将n的值设定为 dest长度-1,并主动将dest的最后一个元素设置为空字符,这样总能安全地得到一个字符串。
也就是以下操作代码:
1 | strncpy(dest, src, sizeof(dest) - 1); |
1 | char src[] = "hello"; |
这样的复制行为,若dest足够装下src,会导致dest剩余部分全都被设置为空字符。
手动实现my_strcpy、my_strncpy
my_strcpy
1 | char* my_strcpy1(char *dest, const char *src) { |
my_strncpy
1 | char *my_strncpy1(char *dest, const char *src, size_t n) { |
字符串拼接strcat,strncat
strcat
函数全名: string_concat,顾名思义,它用于将一个字符串拼接到另一个字符串的末尾。
函数声明:
char *strcat(char *dest, const char *src);
函数作用: strcat 函数会将 src(源字符串)中存储的以空字符 ‘\0’ 结束的字符串拼接到 dest(目标字符串)的末尾。
具体来说:
会从 dest 字符串的空字符 ‘\0’ 开始,替换这个空字符,然后将src中表示字符串的字符数据从开头到空字符,全部拼接到dest末尾。
这个过程中,src 字符串不会被修改,所以它被 const 修饰。
总之,该函数调用后,dest 将包含 dest 和 src 的字符串拼接后的副本。
简单的代码示例:
1 | char dest[20] = "Hello, "; |
函数返回值:
该函数会返回指向目标数组 dest 的指针。一般来说这个返回值可以忽略,但有些场景中可以利用该返回值,对函数调用进行嵌套处理。
例如:
1 | char dest[20] = "Hello, "; |
注意:
- src 和 dest两个参数都必须是字符串,即以空字符串结尾的字符数组,否则将引发未定义行为。
- strcat 函数与 strcpy 一样,不会检查 dest 数组是否能够容纳拼接后的字符串。如果 dest 不够大,就会产生未定义行为,这是潜在的安全隐患。
- 可以考虑使用更安全的strncat函数来实现字符串拼接,它允许指定最大拼接的字符数。
strncat
函数声明:
char *strncat(char *dest, const char *src, size_t n);
函数作用:
仍旧是找到dest字符串末尾的空字符,然后从该字符开始,将 src 的首个元素复制到 dest末尾,直到:
已经复制了 n 个字符。
或者复制到达了
src
字符串的结尾,即遇到了src
的空字符串。所以该函数不会把src中的空字符复制到dest中。
最后,strncat
函数一定会在dest的末尾添加一个空字符,以确保dest能够表示一个字符串。由于这一步操作的存在,该函数仍然具有越界访问的风险,需要程序员自己设定合理的n取值,以规避这种情况。
为了安全的使用此函数,基于函数的特点,我们就可以得出n的计算公式:
1 | int n = sizeof(dest) - strlen(dest) - 1; // dest数组的长度 - 已存储字符串的长度 - 1(留给存空字符) |
表达式中-1
是必要的,否则会导致数组越界情况发生。
给strncat函数传入这样的一个n
参数,就能够保证一定安全的得到一个拼接后的结果字符串。
当然若dest容量不够大,拼接只会截取src的一部分字符数据。但安全是更重要的事情,这样的结果是我们可以接受的。
实际开发中,建议采取以下方式来调用此函数,避免dest空间不足导致溢出:
1 | char src[] = "world"; |
dest最多再拼接(自身数组长度 - 字符串长度 - 1)个字符,最后留一个字节,strncat函数会自动拼接一个空字符表示字符串结束。
手动实现my_strcat、my_strncat
my_strcat
1 | char* my_strcat(char* dest, const char* src) { |
my_strncat
1 |
|
字符串比较大小strcmp
strcmp
函数全名:string_compare,用于比较两个字符串的大小。
函数声明:
int strcmp(const char *str1, const char *str2);
函数作用: strcmp函数按照字典顺序比较两个字符串的大小。当调用 strcmp函数时,它会逐个字符地比较两个字符串 str1 和 str2:
注意:
- 这个函数不会修改任何一个字符串,所以两个参数都被 const 修饰。
- 不同编译器和环境可能对strcmp函数返回值的表现有所不同。在某些环境中(比如VS),出于简化的目的,可能会将返回值归一化为 -1、0 或 1。但是标准的 C 语言库中 strcmp 的返回值是根据实际字符的ASCII值差异来确定的,而不是简单的 -1、0 或 1。
- 要确保传入的两个参数数组都表示字符串,都以空字符结尾,否则可能会导致比较越界。
- strcmp 是区分大小写的比较,如果需要进行不区分大小写的比较,可以使用 strcasecmp 函数。
- 由于strcmp是基于ASCII值进行比较的,它在处理非ASCII或多字节字符(如UTF-8编码的文本)时可能不会表现出预期的行为。
手动实现strcmp
1 | int my_strcmp1(const char* str1, const char* str2) { |
字符相关的库函数
在操作字符串的过程中,经常需要对单个字符做出判断,比如:
- 判断字符是大写字母还是小写
- 判断字符是不是数字
- …
C 标准函数库在头文件 <ctype.h>
中定义了大量此类功能函数。比如:
islower 和 isupper 函数是 C 标准库中声明在 <ctype.h> 头文件中的函数,它们用于检查给定的字符是否为小写字母或大写字母。
除此之外,<ctype.h>
头文件中还包括了以下类似的库函数:
isalpha(int c)
:检查传入的字符是否为字母(包括大写和小写)。isdigit(int c)
:检查传入的字符是否为十进制数字(0到9)。isalnum(int c)
:检查传入的字符是否为字母或数字。isspace(int c)
:检查传入的字符是否为空白字符,比如空格、制表符、换行符等。isblank(int c)
:检查传入的字符是否为空格或制表符。- …