抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

在一些高级语言当中, 譬如 C++、 C#、 Python 等,对字符串的处理支持度很是完善。 若在 C 语言中想要对字符串进行相关的处理,譬如将两个字符串进行拼接、字符串查找、两个字符串进行比较等操作,几乎是需要程序员自己编写字符串处理相关逻辑代码来实现字符串处理功能。

C 语言库函数中已经给我们提供了丰富的字符串处理相关函数,基本常见的字符串处理需求都可以直接使用这些库函数来实现,而不需要自己编写代码,使用这些库函数可以大大减轻编程负担。这些库函数大致可以分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索等几类

1 字符串输入输出

1.1字符串输出

常用的字符串输出函数有 putchar()、 puts()、 fputc()、 fputs(),这些函数只能输出字符串,不能进行格式转换。我们经常使用 printf()函数来输出字符串信息, 可以按照自己规定的格式输出字符串信息,一般称为格式化输出;

puts 函数

puts()函数用来向标准输出设备(屏幕、显示器)输出字符串并自行换行。把字符串输出到标准输出设备,将’ \0 ‘转换为换行符’ \n ‘。 puts 函数原型如下所示(可通过”man 3 puts”命令查看):

1
2
#include <stdio.h>
int puts(const char *s);
  • s: 需要进行输出的字符串。

  • 返回值: 成功返回一个非负数;失败将返回EOF, EOF 其实就是-1。

putchar 函数

putchar()函数可以把参数 c 指定的字符(一个无符号字符)输出到标准输出设备,其输出可以是一个字符,可以是介于 0~127 之间的一个十进制整型数(包含 0 和 127,输出其对应的 ASCII 码字符),也可以是用 char 类型定义好的一个字符型变量。

1
2
#include <stdio.h>
int putchar(int c);
  • c: 需要进行输出的字符。

  • 返回值: 出错将返回 EOF。

fputc 函数

fputc()与 putchar()类似,也用于输出参数 c 指定的字符(一个无符号字符),与 putchar()区别在于, putchar()只能输出到标准输出设备,而 fputc()可把字符输出到指定的文件中,既可以是标准输出、标准错误设备,也可以是一个普通文件。

1
2
#include <stdio.h>
int fputc(int c, FILE *stream);
  • c: 需要进行输出的字符。
  • stream: 文件指针。
  • 返回值: 成功时返回输出的字符;出错将返回EOF。

fputs 函数

同理, fputs()与 puts()类似,也用于输出一条字符串,与 puts()区别在于, puts()只能输出到标准输出设备,而 fputs()可把字符串输出到指定的文件中,既可以是标准输出、标准错误设备,也可以是一个普通文件。

1
2
3
#include <stdio.h>

int fputs(const char *s, FILE *stream);
  • s: 需要输出的字符串。
  • tream: 文件指针。
  • 返回值: 成功返回非负数;失败将返回 EOF。

1.2字符串输入

与 scanf()一样, gets()、 getchar()、 fgetc()、 fgets()这些函数也是标准 I/O 函数,属于标准 C 库函数 。

get函数

gets()函数用于从标准输入设备(譬如键盘)中获取用户输入的字符串。

1
2
#include <stdio.h>
char *gets(char *s);
  • s: 指向字符数组的指针,用于存储字符串。

  • 返回值: 如果成功,该函数返回指向s 的指针;如果发生错误或者到达末尾时还未读取任何字符,则返回 NULL。

gets()与 scanf()的区别

  • gets()函数不仅比 scanf 简洁,而且,就算输入的字符串中有空格也可以,因为 gets()函数允许输入的字符串带有空格、制表符,输入的空格和制表符也是字符串的一部分,仅以回车换行符作为字符串的分割符;而对于 scanf 以%s 格式输入的时候,空格、换行符、 TAB制表符等都是作为字符串分割符存在,即分隔符前后是两个字符串,读取字符串时并不会将分隔符读取出来作为字符串的组成部分,一个%s 只能读取一个字符串,若要多去多个字符串,则需要使用多个%s、并且需要使用多个字符数组存储。

  • gets()会将回车换行符从输入缓冲区中取出来,然后将其丢弃,所以使用 gets()读走缓冲区中的字符串数据之后,缓冲区中将不会遗留下回车换行符;而对于scanf()来说,使用 scanf()读走缓冲区中的字符串数据时,并不会将分隔符(空格、TAB制表符、回车换行符等)读走将其丢弃,所以使用scanf()读走缓冲区中的字符串数据之后,缓冲区中依然还存在用户输入的分隔符。

getchar 函数

getchar()函数用于从标准输入设备中读取一个字符(一个无符号字符),函数原型如下所示:

1
2
#include <stdio.h>
int getchar(void);
  • 函数参数和返回值含义如下:无需传参。

  • 返回值: 该函数以无符号char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。

同样 getchar()函数也是从输入缓冲区读取字符数据,但只读取一个字符,包括空格、 TAB 制表符、换行回车符等。

fgets 函数

fgets()与 gets()一样用于获取输入的字符串, fgets()函数原型如下所示

1
2
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
  • s: 指向字符数组的指针,用于存储字符串。
  • size: 这是要读取的最大字符数。
  • stream: 文件指针。

gets()会将缓冲区中的换行符’\n’读取出来、将其丢弃、将’\n’替换为字符串结束符’\0’; fgets()也会将缓冲区中的换行符读取出来,但并不丢弃,而是作为字符串组成字符存在,读取完成之后自动在最后添加字符串结束字符’\0’。

fgetc 函数

fgetc()与 getchar()一样,用于读取一个输入字符,函数原型如下所示:

1
2
#include <stdio.h>
int fgetc(FILE *stream);
  • stream: 文件指针。
  • 返回值: 该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。

fgetc()与 getchar()的区别在于, fgetc 可以指定输入字符的文件,既可以从标准输入设备输入字符,也可以从一个普通文件中输入字符,其它方面与 getchar 函数相同。

2 字符串长度

C 语言函数库中提供了一个用于计算字符串长度的函数 strlen(),其函数原型如下所示:

1
2
#include <string.h>
size_t strlen(const char *s);

使用该函数需要包含头文件<string.h>。

  • s: 需要进行长度计算的字符串,字符串必须包含结束字符’ \0 ‘。

  • 返回值: 返回字符串长度(以字节为单位),字符串结束字符’ \0 ‘不计算在内

sizeof 和 strlen 的区别

  • sizeof 是 C 语言内置的操作符关键字,而 strlen 是 C 语言库函数;

  • sizeof 仅用于计算数据类型的大小或者变量的大小,而 strlen 只能以结尾为’ \0 ‘

的字符串作为参数;

  • 编译器在编译时就计算出了 sizeof 的结果,而 strlen 必须在运行时才能计算出来;

  • sizeof 计算数据类型或变量会占用内存的大小, strlen 计算字符串实际长度。

3 字符串拼接

C 语言函数库中提供了 strcat()函数或 strncat()函数用于将两个字符串连接(拼接)起来。

strcat 函数:

1
2
#include <string.h>
char *strcat(char *dest, const char *src);

使用该函数需要包含头文件<string.h>。函数参数和返回值含义如下:

  • dest: 目标字符串。

  • src: 源字符串。

  • 返回值: 返回指向目标字符串dest的指针。

strcat()函数会把 src 所指向的字符串追加到 dest 所指向的字符串末尾,所以必须要保证 dest 有足够的存储空间来容纳两个字符串,否则会导致溢出错误; dest 末尾的’ \0 ‘结束字符会被覆盖, src 末尾的结束字符’ \0 ‘会一起被复制过去,最终的字符串只有一个’ \0 ‘。

strncat()函数

strncat()与 strcat()的区别在于, strncat 可以指定源字符串追加到目标字符串的字符数量。

1
2
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
  • dest: 目标字符串。

  • src: 源字符串。

  • n: 要追加的最大字符数。

  • 返回值: 返回指向目标字符串 dest 的指针。

如果源字符串src 包含 n 个或更多个字符,则 strncat()将 n+1 个字节追加到 dest 目标字符串(src 中的 n个字符加上结束字符’ \0 ‘)。

4 字符串拷贝

C 语言函数库中提供了 strcpy()函数和 strncpy()函数用于实现字符串拷贝。

strcpy 函数:

1
2
#include <string.h>
char *strcpy(char *dest, const char *src);
  • dest: 目标字符串。

  • src: 源字符串。

  • 返回值: 返回指向目标字符串 dest 的指针。

strcpy()会把 src(必须包含结束字符’ \0 ‘)指向的字符串复制(包括字符串结束字符’ \0 ‘)到 dest,所以必须保证 dest 指向的内存空间足够大,能够容纳下 src 字符串,否则会导致溢出错误。

strncpy 函数

strncpy()与 strcpy()的区别在于, strncpy()可以指定从源字符串 src 复制到目标字符串 dest 的字符数量, strncpy 函数原型如下所示:

1
2
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
  • dest: 目标字符串。

  • src: 源字符串。

  • n: 从 src 中复制的最大字符数。

  • 返回值: 返回指向目标字符串 dest 的指针。

把src 所指向的字符串复制到 dest,最多复制 n 个字符。当 n 小于或等于 src 字符串长度(不包括结束字符的长度)时,则复制过去的字符串中没有包含结束字符’ \0 ‘;当 n 大于 src 字符串长度时,则会将 src 字符串的结束字符’ \0 ‘也一并拷贝过去,必须保证 dest 指向的内存空间足够大,能够容纳下拷贝过来的字符串,否则会导致溢出错误。

memcpy、 memmove、 bcopy

除了 strcpy()和 strncpy()之外,其实还可以使用 memcpy()、 memmove()以及 bcopy()

这些库函数实现拷贝操作,字符串拷贝本质上也只是内存数据的拷贝,所以这些库函数同样也是适用的,在实际的编程当中,这些库函数也是很常用的,关于这三个库函数,这里不再给大家介绍,用法也非常简单,需要注意的就是目标内存空间与源内存空间是否有重叠的问题。

5 内容填充

在编程中,经常需要将某一块内存中的数据全部设置为指定的值,譬如在定义数组、结构体这种类型变量时,通常需要对其进行初始化操作,而初始化操作一般都是将其占用的内存空间全部填充为 0。

memset 函数

memset()函数用于将某一块内存的数据全部设置为指定的值,其函数原型如下所示:

1
2
#include <string.h>
void *memset(void *s, int c, size_t n);
  • s: 需要进行数据填充的内存空间起始地址。
  • c: 要被设置的值,该值以 int 类型传递。
  • n: 填充的字节数。
  • 返回值: 返回指向内存空间 s 的指针。

参数 c 虽然是以 int 类型传递,但 memset()函数在填充内存块时是使用该值的无符号字符形式,也就是函数内部会将该值转换为 unsigned char 类型的数据,以字节为单位进行数据填充。

bzero 函数

bzero()函数用于将一段内存空间中的数据全部设置为 0,函数原型如下所示:

1
2
#include <strings.h>
void bzero(void *s, size_t n);
  • s: 内存空间的起始地址。
  • n: 填充的字节数。
  • 返回值: 无返回值。

6 字符串比较

C 语言函数库提供了用于字符串比较的函数 strcmp()和 strncmp()。

strcmp()函数

1
2
#include <string.h>
int strcmp(const char *s1, const char *s2);
  • s1: 进行比较的字符串 1。
  • s2: 进行比较的字符串 2。
  • 返回值:
    • 如果返回值小于 0,则表示 str1 小于 str2
    • 如果返回值大于 0,则表示 str1 大于 str2
    • 如果返回值等于 0,则表示字符串 str1 等于字符串 str2

strcmp 进行字符串比较,主要是通过比较字符串中的字符对应的 ASCII 码值, strcmp 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现了不同的字符,或者某一字符串已经到达末尾(遇见了字符串结束字符’ \0 ‘)。

strncmp 函数

strncmp()与 strcmp()函数一样,也用于对字符串进行比较操作,但最多比较前 n 个字符, strncmp()函数原型如下所示:

1
2
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
  • s1: 参与比较的第一个字符串。
  • s2: 参与比较的第二个字符串。
  • n: 最多比较前 n 个字符。
  • 返回值: 返回值含义与 strcmp()函数相同。

7 字符串查找

字符串查找在平时的编程当中也是一种很常见的操作,譬如从一个给定的字符串当中查找某一个字符或者一个字符串,并获取它的位置。 C 语言函数库中也提供了一些用于字符串查找的函数,包括 strchr()、strrchr()、 strstr()、 strpbrk()、 index()以及 rindex()等。

strchr 函数

使用strchr()函数可以查找到给定字符串当中的某一个字符,函数原型如下所示:

1
2
#include <string.h>
char *strchr(const char *s, int c);
  • s: 给定的目标字符串。
  • c: 需要查找的字符。
  • 返回值: 返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。

字符串结束字符’ \0 ‘也将作为字符串的一部分,因此, 如果将参数 c 指定为’ \0 ‘,则函数将返回指向结束字符的指针。 strchr 函数在字符串 s 中从前到后(或者称为从左到右)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置,如果找不到字符 c 就返回 NULL。

strrchr函数

strrchr()与 strchr()函数一样,它同样表示在字符串中查找某一个字符,返回字符第一次在字符串中出现的位置,如果没找到该字符,则返回值 NULL,但两者唯一不同的是, strrchr()函数在字符串中是从后到前(或者称为从右向左)查找字符,找到字符第一次出现的位置就返回,返回值指向这个位置, strrchr()函数原型如下所示:

1
2
#include <string.h>
char *strrchr(const char *s, int c);

函数参数和返回值含义与 strchr()函数相同。

strstr 函数

与strchr()函数不同的是, strstr()可在给定的字符串 haystack 中查找第一次出现子字符串 needle 的位置,不包含结束字符’ \0 ‘,函数原型如下所示:

1
2
#include <string.h>
char *strstr(const char *haystack, const char *needle);
  • haystack: 目标字符串。
  • needle: 需要查找的子字符串。
  • 返回值: 如果目标字符串haystack 中包含了子字符串 needle,则返回该字符串首次出现的位置;如果未能找到子字符串 needle,则返回 NULL。

其它函数

除了上面介绍的三个函数之外,C 函数库中还提供其它的字符串(或字符)查找函数,譬如 strpbrk()、index()以及 rindex()等,这里便不再给大家一一介绍了,这些函数的用法都比较简单,大家通过 man 手册便可以快速了解到它们的使用方法。

8 字符串与数字转换

8.1 字符串转整形数据

C 函数库中提供了一系列函数用于实现将一个字符串转为整形数据,主要包括 atoi()、 atol()、 atoll()以及strtol()、 strtoll()、 strtoul()、 strtoull()等,它们之间的区别主要包括以下两个方面:

  • 数据类型(int、 long int、 unsigned long 等)。

  • 不同进制方式表示的数字字符串(八进制、十六进制、十进制)。

atoi、 atol、 atoll 函数

atoi()、 atol()、 atoll()三个函数可用于将字符串分别转换为 int、 long int 以及 long long

类型的数据,它们的函数原型如下:

1
2
3
4
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
  • nptr: 需要进行转换的字符串。
  • 返回值: 分别返回转换之后得到的int 类型数据、 long int 类型数据以及 long long 类型数据。

目标字符串 nptr 中可以包含非数字字符,转换时跳过前面的空格字符(如果目标字符串开头存在空格字符),直到遇上数字字符或正负符号才开始做转换,而再遇到非数字或字符串结束时(‘ /0 ‘)才结束转换,并将结果返回。

使用 atoi()、 atol()、 atoll()函数只能转换十进制表示的数字字符串,即 0~9。

strtol、 strtoll 函数

strtol()、 strtoll()两个函数可分别将字符串转为 long int 类型数据和 long long ing 类型数据,与 atol()、atoll()之间的区别在于, strtol()、 strtoll()可以实现将多种不同进制数(譬如二进制表示的数字字符串、八进制表示的数字字符串、十六进制表示的数数字符串)表示的字符串转换为整形数据,其函数原型如下所示:

1
2
3
#include <stdlib.h>
long int strtol(const char *nptr, char **endptr, int base);
long long int strtoll(const char *nptr, char **endptr, int base);

使用这两个函数需要包含头文件<stdlib.h>。

  • nptr: 需要进行转换的目标字符串。
  • endptr: char *类型的指针,如果 endptr 不为 NULL,则 strtol()或 strtoll()会将字符串中第一个无效字符的地址存储在endptr 中。如果根本没有数字, strtol()或 strtoll()会将 nptr 的原始值存储在*endptr 中(并返回 0)。也可将参数 endptr 设置为 NULL,表示不接收相应信息。
  • base: 数字基数,参数 base 必须介于 2 和 36(包含)之间,或者是特殊值 0。参数 base 决定了字符串转换为整数时合法字符的取值范围,譬如,当 base=2 时,合法字符为’ 0 ‘、 ‘ 1 ‘(表示是一个二进制表示的数字字符串) ;当 base=8 时,合法字符为’ 0 ‘、 ‘ 1 ‘、 ‘ 2 ‘、 ‘ 3 ‘……’ 7 ‘(表示是一个八进制表示的数字字符串);当 base=16 时,合法字符为’ 0 ‘ 、 ‘ 1 ‘、 ‘ 2 ‘、 ‘ 3 ‘……’ 9 ‘、 ‘ a ‘……’ f ‘(表示是一个十六进制表示的数字字符串);当 base 大于 10 的时候, ‘ a ‘代表 10、 ‘ b ‘代表 11、 ‘ c ‘代表 12,依次类推, ‘ z ‘代表 35(不区分大小写)。
  • 返回值: 分别返回转换之后得到的long int 类型数据以及 long long int 类型数据。

需要进行转换的目标字符串可以以任意数量的空格或者 0 开头,转换时跳过前面的空格字符,直到遇上数字字符或正负符号(’ + ‘或’ - ‘)才开始做转换,而再遇到非数字或字符串结束时(‘ /0 ‘)才结束转换,并将结果返回。

在 base=0 的情况下,如果字符串包含一个了“0x”前缀,表示该数字将以 16 为基数;如果包含的是“0”前缀,表示该数字将以 8 为基数。

当base=16 时,字符串可以使用“0x”前缀。

strtoul、 strtoull 函数

这两个函数使用方法与 strtol()、strtoll()一样,区别在于返回值的类型不同, strtoul()返回值类型是 unsigned long int, strtoull()返回值类型是 unsigned long long int,函数原型如下所示:

1
2
3
#include <stdlib.h>
unsigned long int strtoul(const char *nptr, char **endptr, int base);
unsigned long long int strtoull(const char *nptr, char **endptr, int base);

函数参数与 strtol()、 strtoll()一样,这里不再重述!

8.2 字符串转浮点型数据

C 函数库中用于字符串转浮点型数据的函数有 atof()、 strtod()、 strtof()、 strtold()。

atof 函数

atof()用于将字符串转换为一个 double 类型的浮点数据,函数原型如下所示:

1
2
#include <stdlib.h>
double atof(const char *nptr);

nptr: 需要进行转换的字符串。

返回值: 返回转换得到的double 类型数据。

strtod、 strtof、 strtold 函数

strtof()、 strtod()以及 strtold()三个库函数可分别将字符串转换为 float 类型数据、 double 类型数据、 long double 类型数据,函数原型如下所示:

1
2
3
#include <stdlib.h>
double strtod(const char *nptr, char **endptr); float strtof(const char *nptr, char **endptr);
long double strtold(const char *nptr, char **endptr);

函数参数与strtol()含义相同,但是少了 base 参数。

8.3 数字转字符串

数字转换为字符串推荐大家使用前面介绍的格式化 IO 相关库函数,譬如使用 printf()将数字转字符串、并将其输出到标准输出设备或者使用 sprintf()或 snprintf()将数字转换为字符串并存储在缓冲区中。

9 给应用程序传参

一个能够接受外部传参的应用程序往往使用上会比较灵活,根据参入不同的参数实现不同的功能,前面给大家编写的示例代码中,信息都是硬编码在代码中的,譬如 open 打开的文件路径是固定的,意味着如果需要打开另一个文件则需要修改代码、修改文件路径,然后再重新编译、运行,非常麻烦、不够灵活。其实可以将这些可变的信息通过参数形式传递给应用程序,譬如,当执行应用程序的时候,把需要打开的文件路径作为参数传递给应用程序,就可以在不重新编译源码的情况下,通过传递不同的参数打开不同的文件。当然这里只是举个例子,不同应用程序需根据其需要来设计。

如果在执行应用程序时,需要向应用程序传递参数,则写法如下:

1
2
3
int main(int argc, char **argv) {
/* 代码*/
}

或者写成如下形式:

1
2
3
int main(int argc, char *argv[]) {
/* 代码 */
}

传递进来的参数以字符串的形式存在,字符串的起始地址存储在 argv 数组中,参数 argc 表示传递进来的参数个数,包括应用程序自身路径名,多个不同的参数之间使用空格分隔开来,如果参数本身带有空格、则可以使用双引号” “或者单引号’ ‘的形式来表示。

10 正则表达式

给定一个字符串,检查该字符串是否符合某种条件或规则、或者从给定的字符串中找出符合某种条件或规则的子字符串,将匹配到的字符串提取出来。这种需要在很多的应用程序当中是存在的,例如,很多应用程序都有这种校验功能,譬如检验用户输入的账号或密码是否符合它们定义的规则,如果不符合规则通常会提示用户按照正确的规则输入用户名或密码。

譬如给定一个字符串,在程序当中判断该字符串是否是一个 IP 地址,对于实现这个功能,大家可能首先想到的是,使用万能的 for 循环,当然,使用 for 循环自然是可以解决这个问题,但是在程序代码处理上会比较麻烦。

对于这些需求,其实只需要通过一个正则表达式就可以搞定了

正则表达式,又称为规则表达式(英语: Regular Expression),正则表达式通常被用来检索、替换那些符合某个模式(规则)的字符串,正则表达式描述了一种字符串的匹配模式(pattern

),可以用来检查一个给定的字符串中是否含有某种子字符串、将匹配的字符串替换或者从某个字符串中取出符合某个条件的子字符串。

在Linux 系统下运行命令的时候,相信大家都使用过?或 * 通配符来查找硬盘上的文件或者文本中的某个字符串, ?通配符匹配 0 个或 1 个字符,而 * 通配符匹配 0 个或多个字符,譬如”data?.txt”这样的匹配模式可以将下列文件查找出来:

1
2
3
4
5
data.dat
data1.dat
data2.dat
datax.dat
dataN.dat

正则表达式其实也是一个字符串,该字符串由普通字符(譬如,数字 0~9、大小写字母以及其它字符)和特殊字符(称为“元字符”)所组成,由这些字符组成一个“规则字符串”,这个“规则字符串”用来表达对给定字符串的一种查找、匹配逻辑。

评论