09 字符串、字符和字节

9.1 不受限制的字符串函数

这些函数用NUL来寻找字符串的结尾。在使用这些函数的时候,程序员必须自己保证字符串有正确的结尾,不会溢出内存。

  • 复制字符串

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

  • 连接字符串

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

  • 字符串比较

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

前两个函数的返回值是第一个参数的一份拷贝,即指向目标字符数组的指针的拷贝。比较函数如果s1小于s2返回负数(一般是-1),s1等于s2返回0,s1大于s2返回整数(一般是1)。

9.2 长度受限的字符串函数

下面的函数都会操作到len个长度为止。

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);

注意strncpy如果src的长度超过了len,则最后的NUL不会加上,需要我们手动加。下面的写法保证了dst正确结束。

char buffer[BSIZE];
...
strncpy(buffer, name, BSIZE);
buffer[BSIZE - 1] = '\0';

9.3 字符串查找基础

查找一个字符

第二个函数返回匹配的最后一个字符的指针。如果没有匹配的结果,则返回NULL。

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

查找任何几个字符

返回group中的字符第一次在str中出现的位置。

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

下面的例子中ans最后指向'e'。

char string[20] = "Hello there, honey";
char *ans;
ans = strpbrk(string, "aeiou");

查找一个子串

如果s2是空串,返回s1(即空串是任何串的子串)。如果没有匹配结果,返回NULL。

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

9.4 高级字符串查找

查找一个字符串的前缀

  • strspn返回字符串 str 开头连续包含在 group 中的部分的长度。

  • strcspn该函数返回 str 开头连续都不含字符串 group 中字符的字符数。

这两个函数如果从开头就没有匹配的,则会直接返回0。

size_t strspn(char const *str, char const *group);
size_t strcspn(char const *str, char const *group);
#include<stdio.h>
#include<string.h>
int main ()
{
int len1, len2;
char buffer[] = "1212leo";
char buffer2[] = "leoleo123";
len1 = strspn(buffer, "0123456789");
len2 = strcspn(buffer2, "0123456789");
printf("len1 = %d\n", len1); // 返回4,即1212的长度
printf("len2 = %d\n", len2); // 返回6,即leoleo的长度
return 0;
}

查找标记

sep是作为分隔符的字符集合。strtok函数会修改它所处理的字符串。如果不希望源字符串被修改,则应该使用拷贝。

char *strtok(char *str, char *sep);
#include<stdio.h>
#include<string.h>
void
print_tokens (char *line)
{
static char whitespace[] = " \t\n\r\v\f"; // 注意最前面有个空格
char *token;
for (token = strtok(line, whitespace);
token != NULL;
token = strtok(NULL, whitespace)) {
printf("Next token is %s\n", token);
}
}
int main ()
{
char str[] = "leo love philosophy";
print_tokens (str);
printf("%s\n", str); // 这行输出leo,源字符串已经被修改
return 0;
}

9.5 错误信息

参数是错误码,返回结果是指向描述错误的字符串的指针。

char *strerror(int error_number);

9.6 字符操作

包含两类函数,在头文件ctype.h中。一是用于字符分类,二是用于字符转换

字符分类

  • iscntrl:控制字符

  • isspace:包括空格' ',换页'\f,换行'\n',回车'\r',水平制表符'\t',垂直制表符'\v'

  • isdigit

  • isxdigit:十六进制数,包括0-9和大小写的a-f

  • islower

  • isupper

  • isalpha

  • isalnum

  • ispunct:任何不属于数字或字母的可打印符号

  • isgraph

  • isprint

字符转换

如果参数不合法,则函数会不改变参数,直接返回。

int tolower(int ch);
int toupper(int ch);

9.7 内存操作

void *memcpy(void *dst, void const *src, size_t length); 把从src开始的length个字节复制到dst

void *memmvoe(void *dst, void const *src, size_t length); 同上,但是比上面一个在安全,如果dst和src的地址有重叠的部分,这个函数能正确处理,但是上一个可能出错。

void *memcmp(void const *a, void const *b, size_t length); 按照字节比较,返回结果同strcmp

void *memchr(void const *a, int ch, size_t length); 从a开始查找ch第一次出现的位置。找到返回指针,否则返回NULL

void *memset(void *a, int ch, size_t length); 把从a开始的length字节都设置为ch