前言
本文是针对对字符串有疑惑的初学者。例如:对C语言中的字符串并不了解,不太会使用。学过其他编程语言,现在转入了C语言,但是在C语言中使用字符串时不能像Java一样如愿以偿,自由自在的使用。那么就可以看本篇文章,本篇文章不会涉及太深的东西,太深的东西对于初学者会受不了的。
字符串是谁
说到字符串是谁,那么需要提一下字符是什么,没有字符就没有字符串。
像我们学的“每一个”英文字符(a,b,c…)都是属于字符,并且汉字、数字、标点符号都是属于一个字符;
像“我是谁,我在哪”这7个字符合起来就是一个字符串。那么串的话其实就是多个字符合在一起的结果。
经常听说字符串字面量,究竟是什么鬼?
经常听别人说字符串字面量,字面量的这是啥呢?
其实你一点也磨陌生,可能只是对这个名字陌生一些,你在学习Hello World的时候,其实就在用了。没错用双引号包含的“Hello World”就是字符串字面量。
printf("Hello World!");
执行结果:
Hello World!
我们想换行时,可以加个\n:
printf("Hello \n World!");
结果:
Hello World!
字符串字面量如何存储的
例如字符串:"Hello World!"
你看着是一个字符串,实际上在存储的时候,是像数组一样,一个字符一个字符分开存的。
[“H”,“e”,“l”,“l”,“o”," “,“W”,“o”,“r”,“l”,“d”,”!","\0"];
在存储的时候,每个字符串的结尾是包含"\0",所以一般我们如果不知道一个字符串的长度如何遍历每个字符呢?那就可以使用"\0"条件来判断。
C语言字符数组与字符指针
我们可以利用char类型的数组,来定义和初始化字符串。
char str[12] = "I Love You!"; char *pStr = "I Love You!";
我们在打印的时候,需要使用%s来格式化数值:
printf("str=%s\n",str); printf("pStr=%s\n",pStr);
打印结果:
str=I Love You! pStr=I Love You!
也支持多种单个访问方式:
printf("*(str+2)=%c\n",*(str+2)); printf("*(pStr+2)=%c\n",*(pStr+2));
结果:
*(str+2)=L *(pStr+2)=L
C语言中的字符串库
在Java中有String类型的jar包,在C语言中也有相应的字符串库。无论是Java中的jar包,还是C语言中的库。其实都是一些封装好的工具,以便给他人使用。
在实际开发中,我们掌握这些库的基本用法是必须的,可以大大的提高我们的工作效率。
在Linux中我们可以使用man命令来查看帮助手册。
例如:
man string
会出现下面这样的结果:
可以看到:
这些都是我现在这个Ubuntu系统中的string库中所提供的一些功能的函数。
在此我带着熟悉几个常用的函数。
我挑选了几个常用的,其实也就四种:统计字符串中的字符个数、字符串拼接、字符串比较、字符串拷贝/赋值。
SYNOPSIS #include <string.h> size_t strlen(const char *s); Return the length of the string s. char *strcat(char *dest, const char *src); Append the string src to the string dest, returning a pointer dest. char *strncat(char *dest, const char *src, size_t n); Append at most n bytes from the string src to the string dest, returning a pointer to dest. int strcmp(const char *s1, const char *s2); Compare the strings s1 with s2. int strncmp(const char *s1, const char *s2, size_t n); Compare at most n bytes of the strings s1 and s2. char *strcpy(char *dest, const char *src); Copy the string src to dest, returning a pointer to the start of dest. char *strncpy(char *dest, const char *src, size_t n); Copy at most n bytes from string src to dest, returning a pointer to the start of dest.
我们如何想使用的话,需要先include一下头文件:
本次使用的代码框架如下:
#include <stdio.h> #include <string.h> int main(void) { return 0; }
1、strlen函数
从函数的名字上也不难看出,这个是跟字符串和长度有关系的函数。
如果第一次使用的话,可以使用:man strlen
来查看这个函数的使用说明。
解释的非常详细。
可以看到strlen()函数在计算传入的字符串s的时候是不计算结束符"\0"的。
返回值是传入字符串s的字节数/字符格式。
#include <stdio.h> #include <string.h> int main(void) { char str[12] = "I Love You!"; char *pStr = "I Love You!"; printf("str=%s\n",str); printf("pStr=%s\n",pStr); int strLen = strlen(str); int pStrLen = strlen(pStr); printf("str len:%d \n",strLen); printf("pStr len:%d \n",pStrLen); return 0; }
结果:
可以看到无论是字符数组还是字符指针,都是可以统计出来有多少个字符的
zhenghui@zhlinux:~/桌面/code/string$ zhenghui@zhlinux:~/桌面/code/string$ make strtest && ./strtest cc strtest.c -o strtest str=I Love You! pStr=I Love You! str len:11 pStr len:11 zhenghui@zhlinux:~/桌面/code/string$
计算时到底有没有跟数组的大小有关系?
char str[12] = "I Love You!"; char str2[100] = "I Love You!"; char str3[100] = "I Love You! "; int strLen = strlen(str); int strLen2 = strlen(str2); int strLen3 = strlen(str3); printf("str len:%d \n",strLen); printf("str2 len:%d \n",strLen2); printf("str3 len:%d \n",strLen3);
打印结果:
str len:11 str2 len:11 str3 len:26
可以看到str2和str统计的数量是一样的。
从上面结果来看,数组的大小并不决定这个字符串的长度。
strlen函数只统计“\0”结尾之前的字数,而且不算“\0”这个字符。
2、strcat 和 strncat函数
不了解strcat函数可以直接在linux的命令行中输入:man strcat
就可以看到有两个相关的函数:
SYNOPSIS #include <string.h> char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n);
还有很详细的说文。
The strcat() function appends the src string to the dest string, overwriting the terminating null byte ('\0') at the end of dest, and then adds a terminating null byte. The strings may not overlap, and the dest string must have enough space for the result. If dest is not large enough, program behavior is un‐ predictable; buffer overruns are a favorite avenue for attacking secure programs.
这个strcat函数是用来拼接字符串的,分别传入dest和src字符串,最终把src拼接到dest中进行返回。
实验代码:
zhenghui@zhlinux:~/桌面/code/string$ zhenghui@zhlinux:~/桌面/code/string$ cat strcatTest.c #include <stdio.h> #include <string.h> int main(void) { char str1[12] = " I "; char str2[100] = " Love "; char *pStr = " You!iiiiii "; int sl = strlen(str1); printf("str1 len :%d \n",sl); printf("str1=%s\n",str1); printf("str2=%s\n",str2); printf("pStr=%s\n",pStr); strcat(str1,str2); strcat(str1,pStr); printf("#####################\n"); printf("str1 :%s \n",str1); sl = strlen(str1); printf("str1 len :%d \n",sl); return 0; } zhenghui@zhlinux:~/桌面/code/string$
执行结果:
用法很简单,就是传入两个需要拼接的字符串即可
zhenghui@zhlinux:~/桌面/code/string$ make strcatTest && ./strcatTest cc strcatTest.c -o strcatTest str1 len :3 str1= I str2= Love pStr= You!iiiiii ##################### str1 : I Love You!iiiiii str1 len :21 zhenghui@zhlinux:~/桌面/code/string$
相比较来说,strncat比strcat安全一些。
例如:
如果a的空间是有限的,b的长度又很大。那么就会超出了a的大小。
那么为了安全,就可以使用strncat来指定拼接的大小。
strcat(a,b);
为了安全,我们可以使用strncat:
zhenghui@zhlinux:~/桌面/code/string$ zhenghui@zhlinux:~/桌面/code/string$ zhenghui@zhlinux:~/桌面/code/string$ cat strcatTest.c #include <stdio.h> #include <string.h> int main(void) { char str1[12] = " I "; char str2[100] = " Love "; char *pStr = " You!iiiiii "; int sl = strlen(str1); printf("str1 len :%d \n",sl); printf("str1=%s\n",str1); printf("str2=%s\n",str2); printf("pStr=%s\n",pStr); //strcat(str1,str2); //strcat(str1,pStr); strncat(str1,str2,sizeof(str1) - strlen(str1) - 1); strncat(str1,pStr,sizeof(str1) - strlen(str1) - 1); printf("#####################\n"); printf("str1 :%s \n",str1); sl = strlen(str1); printf("str1 len :%d \n",sl); return 0; } zhenghui@zhlinux:~/桌面/code/string$
可以看到结果也是正常的:
zhenghui@zhlinux:~/桌面/code/string$ make strcatTest && ./strcatTest cc strcatTest.c -o strcatTest str1 len :3 str1= I str2= Love pStr= You!iiiiii ##################### str1 : I Love Y str1 len :11 zhenghui@zhlinux:~/桌面/code/string$
我们使用sizeof(str1) - strlen(str1)就可以计算出str1所剩余的空间,-1是为了给“\0”留出空间。
strncat(str1,str2,sizeof(str1) - strlen(str1) - 1);
3、strcmp 和 strncmp函数
在C语言日常开发中,strcmp是非常常用的,我们做字符串比较,两个字符串是否相等,我们直接调用这个函数就可以很好的解决这个难题。
我们仍然可以使用:man strcmp
来查看帮助手册:
SYNOPSIS #include <string.h> int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); DESCRIPTION The strcmp() function compares the two strings s1 and s2. The locale is not taken into account (for a lo‐ cale-aware comparison, see strcoll(3)). It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. The strncmp() function is similar, except it compares only the first (at most) n bytes of s1 and s2. RETURN VALUE The strcmp() and strncmp() functions return an integer less than, equal to, or greater than zero if s1 (or the first n bytes thereof) is found, respectively, to be less than, to match, or be greater than s2.
这是用来比较两个字符串的。
原型如下:
SYNOPSIS #include <string.h> int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n);
我们需要传递两个字符串s1和s2;
返回值是:
返回值是一个大于、小于或等于0的值。
如果strcmp(s1,s2)==0:说明s1和s2相等;
如果strcmp(s1,s2) > 0:说明s1 > s2;
如果strcmp(s1,s2) < 0:说明s1 < s2;
当然了<=和>=运算符也是可以使用的。
RETURN VALUE The strcmp() and strncmp() functions return an integer less than, equal to, or greater than zero if s1 (or the first n bytes thereof) is found, respectively, to be less than, to match, or be greater than s2.
实验代码:
zhenghui@zhlinux:~/桌面/code/string$ cat strcmpTest.c #include <stdio.h> #include <string.h> int main(void) { char str1[12] = "abc"; char *str2 = "ABC"; printf("str1=%s\n",str1); printf("str2=%s\n",str2); printf("#####################\n"); int res1 = strcmp(str1,str2); printf("strcmp(str1,str2) :%d \n",res1); int res2 = strcmp(str2,str1); printf("strcmp(str2,str1) :%d \n",res2); if(res1 > 0) { printf("res1:%s > %s \n",str1,str2); } if(res2 < 0) { printf("res2:%s < %s \n",str2,str1); } return 0; } zhenghui@zhlinux:~/桌面/code/string$
实验结果:
zhenghui@zhlinux:~/桌面/code/string$ make strcmpTest && ./strcmpTest make: “strcmpTest”已是最新。 str1=abc str2=ABC ##################### strcmp(str1,str2) :32 strcmp(str2,str1) :-32 res1:abc > ABC res2:ABC < abc zhenghui@zhlinux:~/桌面/code/string$
4、strcpy 和 strncpy函数
从字面上看,看着就很想copy拷贝。
没错,这个就是拷贝的功能。
同样,我们使用:man strcpy
来查看帮助手册。
也有很详细的说明:
SYNOPSIS #include <string.h> char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n); DESCRIPTION The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy. Beware of buffer overruns! (See BUGS.) The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated. If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.
废话不多说,直接上代码:
zhenghui@zhlinux:~/桌面/code/string$ zhenghui@zhlinux:~/桌面/code/string$ cat strcpyTest.c #include <stdio.h> #include <string.h> int main(void) { char str1[12] = " abc aowmdi9"; char *str2 = " ABC 9999"; printf("str1=%s\n",str1); printf("str2=%s\n",str2); printf("#####################\n"); strcpy(str1,str2); printf("strcpy(str1,str2):%s \n",str1); return 0; } zhenghui@zhlinux:~/桌面/code/string$
实验结果:
zhenghui@zhlinux:~/桌面/code/string$ make strcpyTest && ./strcpyTest make: “strcpyTest”已是最新。 str1= abc aowmdi9 str2= ABC 9999 ##################### strcpy(str1,str2): ABC 9999 zhenghui@zhlinux:~/桌面/code/string$
通过下面代码可以看出,直接把str2的内容拷贝到了str1中,直接覆盖了。
strcpy(str1,str2);
重点:其实strcpy是用来解决我们不能用赋值运算符来赋值的操作的问题
。
那么什么时候,不能用“=”号来赋值呢?
例如:
我们定义一个数组,一开始不赋值,然后等我们业务逻辑到达的时候,再赋值:
char str3[10];
假设到了该赋值的地方:
str3 = "zhenghui";
这个地方会执行错误的。
可以看下执行结果:
zhenghui@zhlinux:~/桌面/code/string$ make strcpyTest && ./strcpyTest cc strcpyTest.c -o strcpyTest strcpyTest.c: In function ‘main’: strcpyTest.c:11:7: error: assignment to expression with array type 11 | str3 = "zhenghui";//错误 | ^ make: *** [<内置>:strcpyTest] 错误 1 zhenghui@zhlinux:~/桌面/code/string$
像这种特殊的赋值,我们可以利用strcpy来解决:
strcpy(str3,"zhenghui");
执行就没问题了。
如果带参数n的话,就是用来限制copy的数据值的多少,也是为了安全才有的。