字符函数和字符串函数
0. 前言
- C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串
中或者字符数组
中。 字符串常量
适用于那些对它不做修改的字符串函数.
1. 函数介绍
1.1 strlen
size_t strlen ( const char * str );
- 字符串已经
'\0'
作为结束标志,strlen函数返回的是在字符串中'\0'
前面出现的字符个数(不包含'\0'
)。 - 参数指向的字符串必须要以
'\0'
结束。 - 注意函数的返回值为
size_t
,是无符号的(易错)
注:
#include <stdio.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
输出结果为:
srt1>str2
因为strlen
的返回值的类型是size_t
,也就是无符号整型,相减如果本该得到一个负数,反而会得到一个非常大的正数,因此会得到这个输出结果。所以我们在使用size_t
类型的数进行运算的时候,需要注意!
使用实例:
/* strlen example */
#include <stdio.h>
#include <string.h>
int main()
{
char szInput[256];
printf("Enter a sentence: ");
gets(szInput);
printf("The sentence entered is %u characters long.\n", (unsigned)strlen(szInput));
return 0;
}
输出结果:
Enter sentence: just testing
The sentence entered is 12 characters long.
1.2 strcpy
char * strcpy ( char * destination, const char * source );
- Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point). - 源字符串必须以
'\0'
结束。 - 会将源字符串中的
'\0'
拷贝到目标空间。 - 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变
注意:
- 当我们想要将一个字符串的内容拷贝到另一个字符串中时,不能简单地使用赋值运算符,比如
char str1[] = "hello world";
char str2[20] = { 0 };
str2 = str1;
这样编译器会报错,因为str1
,str2
数组名是地址,地址是一个常量值,不能被赋值或者修改,拷贝字符串可以使用strcpy
函数。
- 使用
strcpy
函数时,所传递的第一个参数,也就是目标空间必须可变,比如:
const char *p = "abcdef";
char arr[] = "bit";
strcpy(p, arr);
此时p
指向的是一个常量字符串,目标区域不可修改,因此也会出现拷贝失败
使用实例:
/* strcpy example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
strcpy (str2,str1);
strcpy (str3,"copy successful");
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}
输出结果:
str1: Sample string
str2: Sample string
str3: copy successful
1.3 strcat
char * strcat ( char * destination, const char * source );
- Appends a copy of the source string to the destination string. The terminating null characterin destination is overwritten by the first character of source, and a null-character is includedat the end of the new string formed by the concatenation of both in destination.
- 源字符串必须以
'\0'
结束。 - 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
注意:
- 字符串自己给自己追加会导致错误,比如:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "hello ";
char str2[] = "world!";
strcat(str1, str1);
printf("%s\n", str1);
return 0;
}
这样的程序的输出会出现错误
使用实例:
/* strcat example */
#include <stdio.h>
#include <string.h>
int main()
{
char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");
puts(str);
return 0;
}
输出结果:
these strings are concatenated.
1.4 strcmp
int strcmp ( const char * str1, const char * str2 );
- This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
- 标准规定:
第一个字符串大于
第二个字符串,则返回大于0
的数字
第一个字符串等于
第二个字符串,则返回0
第一个字符串小于
第二个字符串,则返回小于0
的数字
比较两个字符串是否相等不能简单地用==
来判断,即使两个字符串的内容一模一样,使用==
进行判断也会得到不相等的结果
#include <stdio.h>
int main()
{
char str1[20] = "abcdef";
char str2[] = "abcdef";
if (str1 == str2)
printf("==\n");
else
printf("!=\n");
return 0;
}
输出结果:
!=
//str1是数组名,str2是数组名
//数组名是数组首元素的地址,这两个数组的首元素地址不相等
//因此会输出!=
因此比较两个字符串不能使用==
,可以用strcpy
函数来进行判断。
那么如何比较两个字符串?
strcmp
从两个字符串的第一个字符开始比较,比较它们的ASCII码值的大小。- 如果相等,则比较下一个,直至其中一个字符串所有的字符都比较完,那么还没有比较完的那个字符串较大,返回
-1
或者1
。 - 如果两个字符串同时比较完,并且比较过程中每个字符都相等,则返回
0
。 - 如果比较过程中两个字符不相等,则直接返回
-1
或1
使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "ABCD";
char str2[] = "abcd";
int ret1 = strcmp(str1, str2);
if (ret1 == 0)
printf("str1 == str2\n");
else if (ret1 < 0)
printf("str1 < str2\n");
else
printf("str1 > str2\n");
char str3[] = "abcdef";
char str4[] = "abcd";
int ret2 = strcmp(str3, str4);
if (ret2 == 0)
printf("str3 == str4\n");
else if (ret2 < 0)
printf("str3 < str4\n");
else
printf("str3 > str4\n");
char str5[] = "abwde";
char str6[] = "abced";
int ret3 = strcmp(str5, str6);
if (ret3 == 0)
printf("str5 == str6\n");
else if (ret3 < 0)
printf("str5 < str6\n");
else
printf("str5 > str6\n");
char str7[] = "abcd";
char str8[] = "abcd";
int ret4 = strcmp(str7, str8);
if (ret4 == 0)
printf("str7 == str8\n");
else if (ret4 < 0)
printf("str7 < str8\n");
else
printf("str7 > str8\n");
return 0;
}
输出结果:
str1 < str2
str3 > str4
str5 > str6
str7 == str8
1.5 strncpy
char * strncpy ( char * destination, const char * source, size_t num );
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到总共写入num个字符为止。
使用实例:
/* strncpy example */
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "To be or not to be";
char str2[40];
char str3[40];
/* copy to sized buffer (overflow safe): */
strncpy(str2, str1, sizeof(str2));
/* partial copy (only 5 chars): */
strncpy(str3, str2, 5);
str3[5] = '\0'; /* null character manually added */
printf("%s\n", str1);
printf("%s\n", str2);
printf("%s\n", str3);
return 0;
}
输出结果:
To be or not to be
To be or not to be
To be
1.6 strncat
char * strncat ( char * destination, const char * source, size_t num );
- 将源字符串的前num个字符追加到目标字符串,再加上一个终止的
null
字符。 - 如果源中的C字符串的长度小于num,则只复制终止
null
字符之前的内容。
使用实例:
/* strncat example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
printf("%s\n", str1);
return 0;
}
输出结果:
To be or not
1.7 strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
- 比较C字符串
str1
和C字符串str2
的最多num
个字符。 - 比较到出现另个字符不一样或者一个字符串结束或者
num
个字符全部比较完。
使用实例:
/* strncmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)
{
printf ("found %s\n",str[n]);
}
return 0;
}
1.8 strstr
const char * strstr ( const char * str1, const char * str2 ); char * strstr ( char * str1, const char * str2 );
- 返回一个指向str1中第一个出现的str2的指针,如果str2不是str1的一部分,则返回一个空指针。也就是查找子串的一个函数。
- 匹配过程不包括终止的null字符,但仅限于此。
使用实例:
/* strstr example */
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "This is a simple string";
char* pch;
pch = strstr(str, "simple");
puts(pch);
return 0;
}
输出结果:
sample string
1.9 strtok
char * strtok ( char * str, const char * delimiters );
sep
参数是个字符串,定义了用作分隔符的字符集合- 第一个参数指定一个字符串,它包含了0个或者多个由
sep
字符串中一个或者多个分隔符分割的标记。 strtok
函数找到str
中的下一个标记,并将其用\0
结尾,返回一个指向这个标记的指针。(注:strtok
函数会改变被操作的字符串,所以在使用strtok
函数切分的字符串一般都是临时拷贝的内容并且可修改。)strtok
函数的第一个参数不为NULL
,函数将找到str
中第一个标记,strtok
函数将保存它在字符串中的位置。strtok
函数的第一个参数为NULL
,函数将在同一个字符串中被保存的位置开始,查找下一个标记。- 如果字符串中不存在更多的标记,则返回
NULL
指针。
使用实例1:
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
输出结果:
Splitting string "- This, a sample string." into tokens:
This
a
sample
string
使用实例2:
#include <stdio.h>
#include <string.h>
int main()
{
char* p = "www.baidu@com.cn";
const char* sep = ".@";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
}
输出结果:
www
baidu
com
cn
1.10 strerror
char * strerror ( int errnum );
- 返回错误码所对应的错误信息。
- 使用错误码必须包含
errno.h
这个头文件
C语言的库函数,在执行失败的时候,都会设置错误码。errno
是C语言设置的一个全局的错误码存放的变量。strerror
函数解释errnum
的值,生成一个字符串,其中包含一条描述错误条件的消息,就像库的函数设置为errno
一样。
可以使用下面的程序查看每个错误码所代表的信息
#include <stdio.h>
#include <errno.h>
int main()
{
for (int i = 0; i < 50; i++)
printf("errno:%d -> %s\n", i, strerror(i));
return 0;
}
可以看到,C语言一共有43个错误码,其中0
表示程序正常运行,其他错误码分别代表程序出现了不同的错误的信息
errno:0 -> No error
errno:1 -> Operation not permitted
errno:2 -> No such file or directory
errno:3 -> No such process
errno:4 -> Interrupted function call
errno:5 -> Input/output error
errno:6 -> No such device or address
errno:7 -> Arg list too long
errno:8 -> Exec format error
errno:9 -> Bad file descriptor
errno:10 -> No child processes
errno:11 -> Resource temporarily unavailable
errno:12 -> Not enough space
errno:13 -> Permission denied
errno:14 -> Bad address
errno:15 -> Unknown error
errno:16 -> Resource device
errno:17 -> File exists
errno:18 -> Improper link
errno:19 -> No such device
errno:20 -> Not a directory
errno:21 -> Is a directory
errno:22 -> Invalid argument
errno:23 -> Too many open files in system
errno:24 -> Too many open files
errno:25 -> Inappropriate I/O control operation
errno:26 -> Unknown error
errno:27 -> File too large
errno:28 -> No space left on device
errno:29 -> Invalid seek
errno:30 -> Read-only file system
errno:31 -> Too many links
errno:32 -> Broken pipe
errno:33 -> Domain error
errno:34 -> Result too large
errno:35 -> Unknown error
errno:36 -> Resource deadlock avoided
errno:37 -> Unknown error
errno:38 -> Filename too long
errno:39 -> No locks available
errno:40 -> Function not implemented
errno:41 -> Directory not empty
errno:42 -> Illegal byte sequence
errno:43 -> Unknown error
errno:44 -> Unknown error
errno:45 -> Unknown error
errno:46 -> Unknown error
errno:47 -> Unknown error
errno:48 -> Unknown error
errno:49 -> Unknown error
使用实例:
/* strerror example : error list */
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main()
{
FILE* pFile;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
printf("Error opening file unexist.ent: %s\n", strerror(errno));
//errno: Last error number
return 0;
}
输出结果:
Error opening file unexist.ent: No such file or directory
1.11 字符分类函数
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换:
int tolower ( int c );//将大写字母转换为小写字母
int toupper ( int c );//将小写字母转换为大写字母
使用实例:
/* isupper example */
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "Test String.\n";
char c;
while (str[i])
{
c = str[i];
if (isupper(c))
c = tolower(c);
putchar(c);
i++;
}
return 0;
}
输出结果:
test string.
1.12 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
- 函数
memcpy
从source
的位置开始向后复制num
个字节的数据到destination
的内存位置。 - 这个函数在遇到
'\0'
的时候并不会停下来。 - 如果
source
和destination
有任何的重叠,复制的结果都是未定义的。 memcpy
负责拷贝两块独立空间中的数据。strcpy
只能拷贝字符串,而memcpy
可以拷贝任何类型的数据
使用实例:
/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
char name[40];
int age;
} person, person_copy;
int main()
{
char myname[] = "Pierre de Fermat";
/* using memcpy to copy string: */
memcpy(person.name, myname, strlen(myname) + 1);
person.age = 46;
/* using memcpy to copy structure: */
memcpy(&person_copy, &person, sizeof(person));
printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);
return 0;
}
输出结果:
person_copy: Pierre de Fermat, 46
1.13 memmove
void * memmove ( void * destination, const void * source, size_t num );
- 和
memcpy
的差别就是memmove
函数处理的源内存块和目标内存块是可以重叠的。 - 如果源空间和目标空间出现重叠,就得使用
memmove
函数处理。
使用实例:
/* memmove example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
输出结果:
memmove can be very very useful.
1.13 memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
- 将
ptr1
所指向的内存块的前num
个字节与ptr2
所指向的前num
个字节进行比较,如果它们都匹配则返回零,或者如果不匹配则返回与零不同的值,表示哪个值更大。 - 与
strcmp
不同,该函数在找到空字符后不会停止比较。
返回值如下:
使用实例:
/* memcmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}
输出结果;
'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.
1.14 memset
void * memset ( void * ptr, int value, size_t num );
- 将
ptr
指向的内存块的前num
个字节设置为指定值(解释为无符号字符) memset
不适用于整型和浮点型数组的初始化,整型和浮点型数组的初始化最好用for
循环来完成
使用实例:
/* memset example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "almost every programmer should know memset!";
memset (str,'-',6);
puts (str);
return 0;
}
输出结果:
------ every programmer should know memset!