分享

一文讲解C语言字符串

 心本心123 2021-11-18

字符串是一种重要的数据类型,但是 C 语言并未提供显式的字符串数据类型,而是用字符串常量或者字符数组来表示字符串。

图片

字符串表示

字符串是一个或多个字符,并以空字符 '\0' 作为终止符。

char str[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'};

上述字符数组声明了 14 个字符,但初始化中只有 13 个字符,那是因为最后一个字符会自动加上 '\0' 终止符结束,当然,也可以手动加上。

char str[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};

C 语言提供了一种更简洁的方式来对字符数组进行初始化。

char str[] = 'Hello, World!';

字符串常量一般是用双引号 '' 括起来的一串字符来表示,并且不可被修改。

“Hello, World!”

实际上,字符串常量是通过第一个字符的地址存储的,而不是存储字符本身,可以通过 C 语言提供的指针来指向字符串常量。

char *str = 'Hello, World!';

字符串处理

字符串声明与初始化后,还需要处理字符串,如计算字符串长度、字符串复制等操作,C 语言已经提供了处理字符串的库函数,它们的原型位于头文件 string.h 中。

strlen

字符串长度是指它所包含的字符个数。string.h 库函数中提供了计算字符串长度的原型。

size_t strlen(char const *string);

如果想计算长度,如下所示。

char s1[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
strlen(str);    // 13   '\0’ 不计入长度
char s2[] = 'Hello';
char s3[] = 'World!';
strlen(s2);    // 5
strlen(s3);    // 6

strlen 返回一个类型为 size_t 值,该类型在头文件 stddef.h 中定义的,是无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。

if (strlen(s2) >= strlen(s3)) 
    ...
else
    ...

上述结果可以会跳到 else 分支中运行。

if (strlen(s2) - strlen(s3) >= 0)
    ...
else
    ...

上述语句永远不会跳到 else 中去,因为 strlen 的结果是无符号数,操作符 >= 左边的表达式也将是无符号数,而无符号数绝不可能是负,因此将永远为真。

strcpy

想要复制字符串,可以使用库函数 string.h 中提供的 strcpy 函数,原型如下所示。

char *strcpy(char *dst, const char *src);

该函数会把字符串 src 复制到 dst 参数,并返回一个指向目标字符数组的指针拷贝。如果参数 srcdst 在内存中出现重叠,其结果是未定义的。由于 dst 参数将进行修改,所以它必须是个字符数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。

目标参数之前存储的值会被覆盖并丢失。

char s1[] = 'Hello, World!';
char s2[] = 'The Computer';
strcpy(s1, s2);    // The Computer

新字符串以 '\0' 结尾,老字符串 s1 最后剩余的几个字符也被有效地删除。

src 参数长度比目标参数 dst 要长,多余的字符仍被复制,但是会覆盖 dst 后面的内存空间的值。strcpy 无法解决这个问题,因为它无法判断 dst 的长度。

char s1[] = 'The Computer';
char s2[] = 'Hello, World!';
strcpy(s1, s2);    // Hello, World!

strncpy

除了 strcpy,库函数还提供了有长度限制的字符串复制函数,函数原型如下所示。

char *strncpy(char *dst, char const *src, size_t len);

strncpy 函数是通过传递的 len 长度将 src 参数中对应的长度复制到 dst 参数中。如果 src 的长度大于或等于 len,那么会将 len 个字符复制到 dst 中。

char s1[] = 'Hello, World!';
char s2[] = 'Hello, Computer';
strncpy(s1, s2, 10);    // Hello, Comld!

如果 src 的长度小于 lendst 就用额外的 '\0' 填充到 len 长度中。

char s1[] = 'Hello, World!';
char s2[] = 'Hello, Computer';
strncpy(s1, s2, 20);    // Hello, Computer

strncpy 不会向结果中追加 '\0' 结束标记。

char s1[] = 'Hello, World!';
char s2[] = 'Hello, Computer';
strncpy(s1, s2, 20);    // Hello, ComputeHello, World!

因此,使用 strncpy 的时候,最好把 src 中的 '\0' 追加到 dst 中。

strcat

当一个字符串连接到另一个字符串后面时,库函数提供了 strcat 函数来完成,函数原型如下所示。

char *strcat(char *dst, char const *src);

strcat 函数要求 dst 参数有值。如果 srcdst 的位置发生重叠,其结果是未定义的。

char s1[] = 'Hello, World!';
char s2[] = 'The Computer';
strcat(s1, s2);    // Hello, World!The Computer

必须保证 dst 在刨除原先存储字符数组所占的空间后,有足够的剩余空间保存 src整个字符串。

strncat

如果想设置追加到字符数组后的个数,库函数还提供了 strncat 函数来设置长度,函数原型如下所示。

char *strncat(char *dst, char const *src, size_t len);

strncpy 会将 srclen 长度的字符串追加到 dst 后面。

char s1[] = 'Hello, World!';
char s2[] = 'The Computer';
strncat(s1, s2, 9);    // Hello, World!Hello, Co

strncat 总是会在结果字符串后面添加 '\0' 结束符。strncat 最多向目标数组复制 len 个字符,再加上 '\0' 结束符,不会管目标数组中的空间够不够。

strcmp

如果想比较两个字符串,可以使用库函数提供的 strcmp 函数,函数原型如下所示。

int strcmp(char const *s1, char const *s2);

该比较方式是对两个字符串逐个字符进行比较,直到发现不匹配为止。当 s1 < s2,返回负值;当 s1 == s2,返回零值;当 s1 > s2,返回正值;当 s1s2 的一部分,认为 s1 < s2,反之亦然。

char s1[] = 'Hello, World!';
char s2[] = 'Hello, The Computer';
strcmp(s1, s2);    // 1

并未规定用于提示不相等的具体值。只是返回大于零的值或零或小于零的值,并不一定是 1-1

strncmp

strcmp 是比较两个字符串,但没有长度限制,库函数还提供了 strncmp 函数来限制两个字符串比较的长度,函数原型如下所示。

int strncmp(char const *s1, char const *s2, size_t len);

strncmp 最多比较 len 个字符。如果两个字符串在 len 字符之前存在不相等的字符时,函数会停止并返回结果。如果两个字符串在前 len 个字符相等,函数返回零。

char s1[] = 'Hello, World!';
char s2[] = 'Hello, The Computer';
strncmp(s1, s2, 5);    // 0

strchr

当查找字符串中某个字符第一次出现的位置时,可以使用 strchr 函数,函数原型如下所示。

char *strchr(char const *str, int ch);

strchr 查找字符串 str 中第一次出现 ch 的位置,找到后函数返回一个指向该位置的指针。如果不存在,就返回一个 NULL 指针。

char s1[] = 'Hello, World!';
char *ret = strchr(s1, 'o');
printf('o 之后的字符串是:%s', ret);    // o 之后的字符串是:o, World!

strrchr

当查找字符串中某个字符最后一次出现的位置时,可以使用 strrchr 函数,函数原型如下所示。

char *strrchr(char const *str, int ch);

strrchr 是查找字符串 str 中最后一次出现 ch 的位置,找到后函数返回一个指向该位置的指针。如果不存在,就返回一个 NULL 指针。

char s1[] = 'Hello, World!';
char *ret = strrchr(s1, 'o');
printf('o 之后的字符串是:%s', ret);    // o 之后的字符串是:orld!

strpbrk

C 语言库函数提供了查找任意几个字符在字符串中第 1 次出现的位置,函数原型如下所示。

char *strpbrk(char const *str, char const *group);

这个函数返回一个指向 str 中第 1 个匹配 group 中任意一个字符的字符位置。如果未匹配到,函数返回一个 NULL 指针。

char s1[] = 'Hello, World!';
char s2[] = 'Computer';
char *ret = strpbrk(s1, s2);
printf('%s\n', ret);    // ello, World!

如上所示,s1 中的字符 e 是第 1 个匹配 s2 字符数组中的字符,函数返回的是 s1 中字符 e 的位置。

strstr

如果要在字符串中查找一个子串,库函数也提供了 strstr 函数,函数原型如下所示:

char *strstr(char const *s1, char const *s2);

这个函数是查找整个 s2s1 中第 1 次出现的起始位置,并返回一个指向该位置的指针。如果未找到,将返回一个 NULL 指针。

char s1[] = 'Hello, World!';
char s2[] = 'llo, W';
char *ret = strstr(s1, s2);
printf('%s\n', ret);    // llo, World!

当第二个参数是空字符串,函数会返回 s1

char s1[] = 'Hello, World!';
char s2[] = '';
char *ret = strstr(s1, s2);
printf('%s\n', ret);    // Hello, World!

strspn

库函数提供了 strspn 函数用于在字符串的起始位置计算与另一个字符串中任意字符相匹配的个数,函数原型如下所示:

size_t strspn(char const *str, char const *group);

该函数返回 str 起始部分匹配 group 中任意字符的字符数。

char s1[] = 'Hello, World!';
char s2[] = 'Computer';
int len = strspn(s1, s2);
printf('%d\n', len);    // 0

由上可知,printf 打印出来的结果是 0,这是因为 s1 中第一个字符就与 s2 中任意一个字符不匹配。

char s1[] = 'Hello, World!';
char s2[] = 'Homputer';
int len = strspn(s1, s2);
printf('%d\n', len);    // 2

当把 s2 中第 1 个字符改为 H 后,printf 打印出来的结果是 2,这是因为 s1 中第 1 个字符与 s2 中产生匹配,一直到 l,在 s2 中找不到匹配字符,就返回已经匹配上的结果,前缀总共匹配上 2 个字符。

strcspn

strcspn 函数与 strspn 正好相反,它是在字符串的起始位置计算与另一个字符串中任意字符不匹配的个数,函数原型如下所示。

size_t strcspn(char const *str, char const *group);

该函数返回 str 起始部分不匹配 group 中任意字符的字符数。

char s1[] = 'Hello, World!';
char s2[] = 'Computer';
int len = strcspn(s1, s2);
printf('%d\n', len);    // 1

printf 打印出来的结果是 1,这是因为 s1 的第 1 个字符 Hs2 中不存在,而第 2 个字符 es2 中能找到匹配的字符,因此返回的结果为 1

如果使用 '\n\r\f\t\v” 作为 group 参数,该函数将返回 str 中的起始部分所有非空白字符的值。

char s1[] = 'Hello, World!';
char s2[] = '\n\r\f\t\v';
int len = strcspn(s1, s2);
printf('%d\n', len);    // 13

strtok

当想要对字符串进行分割的时候,可以使用库函数提供的 strtok 函数,函数原型如下所示:

char *strtok(char *str, char const *sep);

这个函数会通过传入的 sep 参数作为分隔符,对传入的 str 字符串找到下一个分隔符,并将其用 '\0' 结尾,然后返回一个指向这个分割后的字符串指针,如果无法分割,就返回 NULL 指针。

char s[] = 'Desire causes suffering because it can never be completely gratified.';
char *token;
for (token = strtok(s, ' ');  token != NULL ;token = strtok(NULL, ' ')) {
    printf('%s\n', token);
}
/*
Desire
causes
suffering
because
it
can
never
be
completely
gratified.
*/

如上所示,strtok 函数的第 1 个参数不是 NULL 时,函数找到分隔符,并且 strtok 同时将保存它在字符串中的位置;strtok 函数的第 1 个参数是 NULL,函数就在同一个字符串中从这个被保存的位置开始查找下一个分隔符;当再也找不到分隔符后,strtok 函数返回一个 NULL 指针结束分割操作。

strtok 会修改它所处理的字符串,如果想原字符串不被修改,那就复制一份,在使用 strtok 函数。 strtok 还会保存局部状态信息,不能同时分割两个字符串。

图片
海人为记
专注知识学习与分享,希望与您携手共同成长!
88篇原创内容
公众号

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多