比较字符串

strcmp()

函数原型:**int (const char _Str1,const char _Str2);

函数要比较的是字符串的内容,不是字符串的地址。
如果在机器排序序列(通常是ASCII值)中第1个字符串位于第2个字符串前面,strcmp()中就返回负数,表面第一个字符小于第2个字符,反之,strcmp()则返回正数。如果两个字符串开始的几个字符都相同,strcmp()会依次比较每个字符,直到发现第1对不同的字符为止。然后,返回相应的值。

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

int main(){
char s1[]="hello world";
char s2[]="hello woRld";
char s3[]="hello world";
printf("%d\n",strcmp(s1,s2));//r位于R的后面,r的ASCII值大于R的ASCII值
printf("%d\n",strcmp(s2,s1));
printf("%d\n",strcmp(s3,s1));
}

strncmp()

函数原型:**int strncmp(const char _Str1,const char _Str2,size_t _MaxCount);

比较字符串str1 和str2 中至多count 个字符,如果参数中任一字符串长度小于count, 那么当比较到第一个空值结束符时,就结束处理。

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

int main(){
char s1[]="hello world";
char s2[]="hello woRld";
char s3[]="hello world HellO";
printf("%d\n",strncmp(s1,s2,5));
printf("%d\n",strncmp(s2,s1,10));
printf("%d\n",strncmp(s3,s1,15));
}

拼接字符串

strcat()

函数原型为:char * strcat(char * Dest,const char * Source);

strcat()函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,返回值为第1个参数,即拼接第2个字符串后的第1个字符串的地址。

strcat()函数无法检查第1个数组是否能容纳第2个字符串。如果分配给第1个数组的空间不够大,多出来的字符溢出到相邻存储单元时就会出问题。

(代码中的m_fgets()函数是对fgets()的处理,这里可以当作是gets()的替换,了解详情看c语言输出输出一文

#include <stdio.h>
#include <string.h>
char* m_fgets(char s[], int n) {
char* ret = NULL;
int i = 0;
ret = fgets(s, n, stdin);
if (ret) {
while (s[i] != '\0' && s[i] != '\n')i++;
if (s[i] == '\n') s[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
int main()
{
char d[20];
char s[10] = "zxcvbnm.";
m_fgets(d, sizeof(d));
fputs(d, stdout);
strcat(d, s);
printf("\nd=%s\n", d);

char a[20];
char b[10] = "zxcvbnm.";
m_fgets(a, sizeof(a));
fputs(a, stdout);
strcat(a, b);
printf("\nd=%s\n", a);
}

输入输出结果

strncat()

函数原型:*char strncat(char * Dest,const char * Source,size_t _Count)

用strncat()函数的第3个参数指定了最大添加字符数。例如,strncat(d,s,5)将把s 字符串的内容附加给d,在加到第5个字符或遇到空字符'\0'时停止。因此,算上空字符d数组应该足够大,以容纳原始字符串(不包含空字符)、添加原始字符串在后面的5字符和末尾的空字符。

建议n的取值为目标数组大小-字符串长度-1

(代码中的m_fgets()函数是对fgets()的处理,这里可以当作是gets()的替换,了解详情看c语言输出输出一文

#include <stdio.h>
#include <string.h>
char* m_fgets(char s[], int n) {
char* ret = NULL;
int i = 0;
ret = fgets(s, n, stdin);
if (ret) {
while (s[i] != '\0' && s[i] != '\n')i++;
if (s[i] == '\n') s[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
int main()
{
char d[20];
char s[10] = "zxcvbnm.";
m_fgets(d, sizeof(d));
fputs(d, stdout);
strncat(d,s,5);//将s字符串的内容附加给d,加到第5个字符停止
printf("\nd=%s\n", d);

char a[20];
char b[10] = "zx";
m_fgets(a, sizeof(a));
fputs(a, stdout);
strncat(a, b, 5);//将s字符串的内容附加给d,未加到第5个字符时遇到空字符停止
printf("\na=%s\n", a);
}

输入输出结果

拷贝字符串

strcpy()

函数原型:*char * strcpy(char Dest,const char * Source);

strcpy()函数相当于字符串赋值运算符,strcpy()第2个参数指向的字符串被拷贝至第1个参数指向的数组中。第1个参数不必指向数组的开始,可用于拷贝数组的部分。strcpy()把源字符串中的空字符’\0’也拷贝在内

#include <stdio.h>
#include <string.h>
int main()
{
char d[15]="what is this?";
char s[10] = "hello";
strcpy(d+3,s);
puts(d);
}

strncpy

函数原型:**char strncpy(char target,const char * Source,size_t _Count)

strcpy()和strcat()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。strncpy()更安全,该函数的第3个参数指明可拷贝的最大字符数。

如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是,strncpy()拷贝字符串的长度不会超过n

#include <stdio.h>
#include <string.h>
int main()
{
char d[14]="what is this?";
char s[5] = "hell";
strncpy(d,s,14);
puts(d);
}

如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝的副本中不一定有空字符。

拷贝的目标数组中没有空字符结尾,D就不是一个字符串,例如下代码

#include <stdio.h>
#include <string.h>
int main()
{
char d[10]="what is t";
char s[15] = "helloworld";
strncpy(d,s,10);
puts(d);
}

鉴于此,把n设置为比目标数组大小少1,然后把数组最后一个元素设置为空字符\0,确保储存的是一个字符串。

#include <stdio.h>
#include <string.h>
int main()
{
char d[10]="what is t";
char s[15] = "helloworld";
strncpy(d,s,10-1);
d[10]='\0';
puts(d);
}
  • 如果目标空间D能容纳源字符串的副本,那么从源字符串S拷贝的空字符便是该副本的结尾:
  • 如果目标空间D装不下副本,则把副本S最后一个元素设置为空字符。

切割字符串

strtok()

函数原型:char *strtok(char *str, const char *delim);

  • str:要分割的字符串。如果是第一次调用,需要传入待分割的字符串;后续调用应传入 NULL

  • delim:一个包含所有分隔符的字符串。

  • strtok 会在原字符串中插入 \0 字符,以终止每个标记,因此原字符串会被修改。

  • 返回值为指向下一个标记的指针。如果没有更多的标记可供提取,则返回 NULL

    (标记是由一系列字符组成的子字符串,这些字符被分隔符分开。例如,在字符串 "Hello, world!" 中,逗号和空格是分隔符,标记包括 "Hello""world"

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

int main() {
char str[] = "Hello, world! Welcome to C programming.";
const char *delim = " ,.!"; // 定义分隔符
char *token;

// 获取第一个子字符串
token = strtok(str, delim);

// 遍历所有子字符串
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok(NULL, delim); // 获取下一个子字符串
}

return 0;
}

输出结果

strtok 不是线程安全的。如果需要在多线程环境中使用,可以考虑使用 strtok_r

strtok_r()

函数原型:**char *strtok_r(char str, const char delim, char **saveptr);strtok_r 需要额外的参数来维护上下文,这使得它在多线程环境中更安全

  • str:要分割的字符串。如果是第一次调用此函数,这个参数传入待分割的字符串;后续调用时传入 NULL
  • delim:分隔符字符串,包含所有可能用作分隔的字符。
  • saveptr:指向 char* 指针的指针,用于保存上下文信息,以便后续调用继续从上次停止的位置开始。
#include <stdio.h>
#include <string.h>

int main() {
char str[] = "Hello, world! Welcome to C.";
const char *delim = " ,.!"; // 定义分隔符
char *token;
char *saveptr; // 保存上下文

// 第一次调用,获取第一个标记
token = strtok_r(str, delim, &saveptr);
printf("saveptr: %s\n", saveptr);
// 遍历所有标记
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok_r(NULL, delim, &saveptr); // 后续调用
printf("saveptr: %s\n", saveptr);
}

return 0;
}

sscanf()

函数原型:**int sscanf(const char str, const char format, …);

返回值:成功读取的项目数。如果没有读取任何项,则返回 0;如果发生错误,则返回 EOF。

作用:从字符串中读取格式化的数据

#include<stdio.h>
int main(){
char log_line[] = "2024-09-28 12:34:56 Error: Something went wrong.";
char date[11];
char time[9];
char message[100];
sscanf(log_line, "%10s %8s %99[^\n]", date, time, message);
printf("date=%s\ntime=%s\nmessage=%s",date,time,message);
}

sprintf()

函数原型:**int sprintf(char str, const char format, …);

返回值为写入到字符串的字符总数,不包括终止的空字符。

sprintf()函数格式化字符串并写入到str中。

#include<stdio.h>
int main(){
char s[100] ;
char date[11]="2024-09-28";
char time[9]="12:34:56";
char message[100]="Error: Something went wrong.";
sprintf(s,"date=%s time=%s message=%s\n",date,time,message);
printf("s=%s",s);
}

常见的字符串操作

  1. char * strchr(const char * s,int c); 字符查找

    如果s字符串中包含c字符,该函数返回指向s字符串第一次出现的位置的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。

  2. char* strrchr(const char* s,int c);字符查找
    该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。

  3. char * strpbrk(const char *s1,const char *s2);字符查找
    如果s1字符中包含s2字符串中的任意字符,该函数返回指向s1字符串首位置的指针:如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。

  4. char *strstr(const char*s1,const char*s2);子字符串查找
    该函数返回指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针

  5. int atoi(const char *str);将字符串转换为int整数

    如果字符串仅以整数开头,atio()函数也能处理,它只把开头的整数转换为字符。例如atoi(“42regular”)将返回整数 42。**如果字符串无法转换(例如,前导字符不是数字),则返回 0。然而C标准规定,这种情况下的行为是未定义的。**因此,使用有错误检测功能的strtol()函数会更安全。

  6. long strtol(const char *str, char **endptr, int base);将字符串转换为int整数

    endptr指向字符指针的指针,用于存储指向解析后字符串中未转换部分的指针;base表示进制,支持 2 到 36 之间的值

    返回转换后的长整型值。如果没有可转换的数字,则返回 0。如果发生溢出,则返回 LONG_MAXLONG_MIN,并设置 errnoERANGE,示例代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>

    int main() {
    const char *numStr = "12345well";
    char *endptr;
    long num = strtol(numStr, &endptr, 10);

    if (*endptr != '\0') {
    printf("Converted number: %ld\n", num);
    printf("Conversion stopped at: %s\n", endptr);
    } else {
    printf("Converted number: %ld\n", num);
    }
    printf("------------------------------------------------------\n");
    // 处理溢出
    const char *largeNumStr = "99999999999999999999"; // 超过 long 的范围
    errno = 0; // 重置 errno
    long largeNum = strtol(largeNumStr, NULL, 10);

    if (errno == ERANGE) {
    printf("Overflow occurred!\n");
    } else {
    printf("Converted large number: %ld\n", largeNum);
    }

    return 0;
    }
  7. 在 C 语言中,可以使用标准库函数将整数转换成字符串。最常用的方法是使用 sprintfsnprintf 函数。在一些编译器中,你可能还可以使用 itoa 函数(例如在某些 MSVC 编译器中),不过它不是标准 C 函数。

    #include <stdlib.h>
    #include<stdio.h>
    int main() {
    int num = 12345;
    char str[20];

    itoa(num, str, 10); // 将整数转换为字符串, 10
    printf("%s\n", str);
    return 0;
    }
    //10

ASCII表

ASCII(美国标准信息交换码)是一种字符编码标准,用于表示文本中的字符。

十进制 十六进制 字符 说明
0 00 NUL 空字符
1 01 SOH 标题开始
2 02 STX 正文开始
3 03 ETX 正文结束
4 04 EOT 传输结束
5 05 ENQ 查询
6 06 ACK 确认
7 07 BEL 响铃
8 08 BS 退格
9 09 TAB 水平制表符
10 0A LF 换行
11 0B VT 垂直制表符
12 0C FF 换页
13 0D CR 回车
14 0E SO 移出
15 0F SI 移入
16 10 DLE 数据链路转义
17 11 DC1 设备控制1
18 12 DC2 设备控制2
19 13 DC3 设备控制3
20 14 DC4 设备控制4
21 15 NAK 拒绝确认
22 16 SYN 同步
23 17 ETB 传输块结束
24 18 CAN 取消
25 19 EM 结束媒介
26 1A SUB 替代
27 1B ESC 转义
28 1C FS 文件分隔符
29 1D GS 组分隔符
30 1E RS 记录分隔符
31 1F US 单元分隔符
32 20 (space) 空格
33 21 ! 感叹号
34 22 双引号
35 23 # 井号
36 24 $ 美元符号
37 25 % 百分号
38 26 &
39 27 单引号
40 28 ( 左括号
41 29 ) 右括号
42 2A * 星号
43 2B + 加号
44 2C , 逗号
45 2D - 减号
46 2E .
47 2F / 斜杠
48 30 0 数字0
49 31 1 数字1
50 32 2 数字2
51 33 3 数字3
52 34 4 数字4
53 35 5 数字5
54 36 6 数字6
55 37 7 数字7
56 38 8 数字8
57 39 9 数字9
58 3A : 冒号
59 3B ; 分号
60 3C < 小于号
61 3D = 等号
62 3E > 大于号
63 3F ? 问号
64 40 @ 井字号
65 41 A 大写字母A
66 42 B 大写字母B
67 43 C 大写字母C
68 44 D 大写字母D
69 45 E 大写字母E
70 46 F 大写字母F
71 47 G 大写字母G
72 48 H 大写字母H
73 49 I 大写字母I
74 4A J 大写字母J
75 4B K 大写字母K
76 4C L 大写字母L
77 4D M 大写字母M
78 4E N 大写字母N
79 4F O 大写字母O
80 50 P 大写字母P
81 51 Q 大写字母Q
82 52 R 大写字母R
83 53 S 大写字母S
84 54 T 大写字母T
85 55 U 大写字母U
86 56 V 大写字母V
87 57 W 大写字母W
88 58 X 大写字母X
89 59 Y 大写字母Y
90 5A Z 大写字母Z
91 5B [ 左中括号
92 5C \ 反斜杠
93 5D ] 右中括号
94 5E ^ 逻辑异或
95 5F _ 下划线
96 60 ` 反引号
97 61 a 小写字母a
98 62 b 小写字母b
99 63 c 小写字母c
100 64 d 小写字母d
101 65 e 小写字母e
102 66 f 小写字母f
103 67 g 小写字母g
104 68 h 小写字母h
105 69 i 小写字母i
106 6A j 小写字母j
107 6B k 小写字母k
108 6C l 小写字母l
109 6D m 小写字母m
110 6E n 小写字母n
111 6F o 小写字母o
112 70 p 小写字母p
113 71 q 小写字母q
114 72 r 小写字母r
115 73 s 小写字母s
116 74 t 小写字母t
117 75 u 小写字母u
118 76 v 小写字母v
119 77 w 小写字母w
120 78 x 小写字母x
121 79 y 小写字母y
122 7A z 小写字母z
123 7B { 左花括号
124 7C | 竖线
125 7D } 右花括号
126 7E ~ 波浪号
127 7F DEL 删除字符

大部分控制字符(如 NUL、BEL 等)用于特殊目的,常用字符包括字母、数字和标点符号。