# 字符串处理

#include <string.h>
// strlen()函数返回字符串的字节长度,不包括末尾的空字符\0。
size_t strlen(const char* s);
// 字符串的复制,不能使用赋值运算符,直接将一个字符串赋值给字符数组变量。
strcpy(char dest[], const char source[]);
// strncpy()跟strcpy()的用法完全一样,只是多了第3个参数,用来指定复制的最大字符数,防止溢出目标字符串变量的边界。
char* strncpy( char* dest, char* src, size_t n);
strncpy(str1, str2, sizeof(str1) - 1);
// strncpy()不会自己添加\0,如果复制的字符串片段不包含结尾标志,就需要手动添加。
str1[sizeof(str1) - 1] = '\0';
// 连接字符串
// 返回值是一个字符串指针,指向第一个参数。
char* strcat(char* s1, const char* s2);
// strncat()用于连接两个字符串,用法与strcat()完全一致,只是增加了第三个参数,指定最大添加的字符数。在添加过程中,一旦达到指定的字符数,或者在源字符串中遇到空字符\0,就不再添加了。
char* strncat(
  char* dest,
  const char* src,
  size_t n
);
// strncat()总是会在拼接结果的结尾,自动添加空字符\0,
// 所以第三个参数的最大值,应该是str1的变量长度减去str1的字符串长度,再减去1。
// strncat 写入 n+1 个字符
strncat(
  str1,
  str2,
  sizeof(str1) - strlen(str1) - 1
);
// strcmp()函数用于比较两个字符串的内容。
int strcmp(const char* s1, const char* s2);
// 如果两个字符串相同,返回值为0;如果s1小于s2,strcmp()返回值小于0;如果s1大于s2,返回值大于0。
// 只比较到指定的位置。
int strncmp(
  const char* s1,
  const char* s2,
  size_t n
);

// sprintf()函数跟printf()类似,但是用于将数据写入字符串,而不是输出到显示器。
int sprintf(char* s, const char* format, ...);

char first[6] = "hello";
char last[6] = "world";
char s[40];

sprintf(s, "%s %s", first, last);

printf("%s\n", s); // hello world

// snprintf()只比sprintf()多了一个参数n,用来控制写入变量的字符串不超过n - 1个字符,剩下一个位置写入空字符\0。下面是它的原型。
// snprintf()总是会自动写入字符串结尾的空字符。如果你尝试写入的字符数超过指定的最大字符数,snprintf()会写入 n - 1 个字符,留出最后一个位置写入空字符。
// snprintf() 写入 n-1 个字符,最后一个写入 \0。
int snprintf(char*s, size_t n, const char* format, ...);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# strchr(),strrchr()

strchr()和strrchr()都用于在字符串中查找指定字符。 不同之处是,strchr()从字符串开头开始查找,strrchr()从字符串结尾开始查找, 函数名里面多出来的那个r表示 reverse(反向)。

char* strchr(char* str, int c);
char* strrchr(char *str, int c);

char *str = "Hello, world!";
char *p;

p = strchr(str, ',');  // p 指向逗号的位置
p = strrchr(str, 'o'); // p 指向 world 里面 o 的位置
1
2
3
4
5
6
7
8

# strspn(),strcspn()

strspn()用来查找属于指定字符集的字符串长度,strcspn()正好相反,用来查找不属于指定字符集的字符串长度。

size_t strspn(char* str, const char* accept);
size_t strcspn(char *str, const char *reject);

char str[] = "hello world";
int n;

n = strspn(str1, "aeiou");
printf("%d\n", n);  // n == 0

n = strcspn(str1, "aeiou");
printf("%d\n", n); // n == 1
1
2
3
4
5
6
7
8
9
10
11

上面示例中,第一个n等于0,因为0号位置的字符h就不属于指定字符集aeiou, 可以理解为开头有0个字符属于指定字符集。第二个n等于1, 因为1号位置的字符e属于指定字符集aeiou,可以理解为开头有1个字符不属于指定字符集。

# strpbrk()

strpbrk()在字符串中搜索指定字符集的任一个字符。

// 它接受两个参数,第一个参数是源字符串,第二个参数是由指定字符组成的字符串。

// 它返回一个指向第一个匹配字符的指针,如果未找到匹配字符,则返回 NULL。
char* strpbrk(const char* s1, const char* s2);

char* s1 = "Hello, world!";
char* s2 = "dow!";

char* p = strpbrk(s1, s2);

printf("%s\n", p);  // "o, world!"
1
2
3
4
5
6
7
8
9
10
11

# strstr()

char *strstr(
  const char* str,
  const char* substr
);

// 如果匹配成功,就返回一个指针,指向源字符串里面的子字符串。
// 如果匹配失败,就返回 NULL,表示无法找到子字符串。
char* str = "The quick brown fox jumped over the lazy dogs.";
char* p = strstr(str, "lazy");

printf("%s\n", p == NULL ? "null": p); // "lazy dogs."
1
2
3
4
5
6
7
8
9
10
11

# strtok()

strtok()用来将一个字符串按照指定的分隔符(delimiter),分解成一系列词元(tokens)。

// 它接受两个参数,第一个参数是待拆分的字符串,第二个参数是指定的分隔符。

// 它返回一个指针,指向分解出来的第一个词元,
// 并将词元结束之处的分隔符替换成字符串结尾标志\0。如果没有待分解的词元,它返回 NULL。
char* strtok(char* str, const char* delim);

#include <stdio.h>
#include <string.h>

int main(void) {
  char string[] = "This is a sentence with 7 tokens";
  char* tokenPtr = strtok(string, " ");

  while (tokenPtr != NULL) {
    printf("%s\n", tokenPtr);
    tokenPtr = strtok(NULL, " ");
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# strerror()

strerror()函数返回特定错误的说明字符串。

char *strerror(int errornum);
//它的参数是错误的编号,由errno.h定义。返回值是一个指向说明字符串的指针。
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(void) {
  FILE* fp = fopen("NONEXISTENT_FILE.TXT", "r");

  if (fp == NULL) {
    char* errmsg = strerror(errno);
    printf("Error %d opening file: %s\n", errno, errmsg);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# memcpy()

memcpy()用于将一块内存拷贝到另一块内存。该函数的原型定义在头文件string.h。

void* memcpy(
  void* restrict dest,
  void* restrict source,
  size_t n
);

#include <stdio.h>
#include <string.h>

int main(void) {
  char s[] = "Goats!";
  char t[100];

  memcpy(t, s, sizeof(s));  // 拷贝7个字节,包括终止符

  printf("%s\n", t);  // "Goats!"

  char* s = "hello world";

  size_t len = strlen(s) + 1;
  char *c = malloc(len);

  if (c) {
    // strcpy() 的写法
    strcpy(c, s);

    // memcpy() 的写法
    memcpy(c, s, len);
  }

  return 0;
}
// memcpy()可以取代strcpy()进行字符串拷贝,
// 而且是更好的方法,不仅更安全,速度也更快,它不检查字符串尾部的\0字符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# memmove()

memmove()函数用于将一段内存数据复制到另一段内存。它跟memcpy()的主要区别是, 它允许目标区域与源区域有重叠。 如果发生重叠,源区域的内容会被更改;如果没有重叠,它与memcpy()行为相同。

void* memmove(
  void* dest,
  void* source,
  size_t n
);

int a[100];
// ...

// 从数组成员a[1]开始的99个成员,都向前移动一个位置。
memmove(&a[0], &a[1], 99 * sizeof(int));

char x[] = "Home Sweet Home";

// 输出 Sweet Home Home
printf("%s\n", (char *) memmove(x, &x[5], 10));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# memcmp()

memcmp()函数用来比较两个内存区域。

int memcmp(
  const void* s1,
  const void* s2,
  size_t n
);

// 它的返回值是一个整数。两块内存区域的每个字节以字符形式解读,按照字典顺序进行比较,
// 如果两者相同,返回0;如果s1大于s2,返回大于0的整数;如果s1小于s2,返回小于0的整数。

char* s1 = "abc";
char* s2 = "acd";
int r = memcmp(s1, s2, 3); // 小于 0

char s1[] = {'b', 'i', 'g', '\0', 'c', 'a', 'r'};
char s2[] = {'b', 'i', 'g', '\0', 'c', 'a', 't'};

if (memcmp(s1, s2, 3) == 0) // true
if (memcmp(s1, s2, 4) == 0) // true
if (memcmp(s1, s2, 7) == 0) // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# memchr()

memchr()用于在内存区域中查找指定字符。

void* memchr(const void* s, int c, size_t n);

char *str = "Hello, world!";
char *p;

p = memchr(str, '!', 13); // p 指向感叹号的位置
1
2
3
4
5
6

# memset()

memset()将一段内存全部格式化为指定值。

void* memset(void* s, int c, size_t n);


char string1[15] = "BBBBBBBBBBBBBB";

// 输出 bbbbbbbBBBBBBB
printf("%s\n", (char*) memset(string1, 'b', 7));

memset(arr, 0, sizeof(arr));
1
2
3
4
5
6
7
8
9