Navigator
- 一、strlen()函数—统计长度
- 二、strcat()函数—拼接
- 三、strncat()函数—strcat()的升级
- 四、strcmp()和strncmp()—比较
- 五、strcpy()和strncpy()—拷贝
- 六、sprintf()函数—合并多个字符串
- 七、其他可能用到的字符串函数
- 八、ctype.h中的字符函数
- 九、把字符串转换为数字
- 十、命令行参数
- 十一、练习—字符串排序
- 附录:
C库提供了多个处理字符串的函数,ANSI C把这些函数的原型放在string.h
头文件中。其中最常用的函数有strlen()、strcat()、strcmp()、strncmp()、strcpy()和strncpy()
。另外,还有sprintf()函数,其原型在stdio.h头文件中。
本文末尾附有:string.h系列函数的完整列表。
一、strlen()函数—统计长度
strlen()函数用于统计字符串的长度,但是不把字符串结尾字符 ‘\0’ 计算在内。(‘\0’ 实际上是占用存储空间的)。
(1)当字符串用数组形式定义,且根据初始化字符串分配存储空间时:
- strlen()函数计算字符串除了 ‘\0’ 字符的长度;
- sizeof()函数计算字符串实际占用内存的字节数,包括结尾的 ‘\0’ ;
- 如果字符串使用指针形式定义:strlen()函数不变,sizeof()函数计算的是这个指针变量本身的大小(而不是它指向的那个字符串的大小),64位系统是8个字节;
- 对于字符数组:两者都可以计算字符数组的元素个数,且相等(因为字符数组没有’\0’ 结束符)。
(2)当字符串用数组形式定义,且指定长度时:
- strlen()函数用法不变;
- .直接使用sizeof()函数,返回的值与指定数组大小的值相同,因为程序根据指定的值分配内存,而不管你的字符串是否填满了这个空间。使用
sizeof(arr)/sizeof(char)
则可以获得该字符串实际占用的空间大小(包括’\0’)。
总之,两者都可以计算出字符串中字符的个数(包括空格、换行等不可见字符)。但是sizeof()函数的结果比strlen()多1(即多了’\0’字符串结束符,实际占用1个字节内存),具体看你怎么用了。
示例:
void strlen_test()
{
char s1[] = "I am dog.";
char* s2 = "I am dog.";
char s3[] = {'H','e','l','l','0',' '};
char s4[20] = "Hello, C.";
printf("%zd\n",strlen(s1));
printf("%zd\n", sizeof(s1));
printf("%zd\n", strlen(s2));
printf("%zd\n", sizeof(s2));
printf("%zd\n", strlen(s3));
printf("%zd\n", sizeof(s3));
printf("%zd\n", strlen(s4));
printf("%zd\n", sizeof(s4));
printf("%zd\n", sizeof(s1)/sizeof(char));
}
输出:
9
10
9
8
6
6
9
20
10
二、strcat()函数—拼接
strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char *(即,指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。
注意:
创建字符串数组 的时候,就给他分配好内存了,要么你指定大小,要么根据初始化字符串常量自动分配大小。所以:使用strcat()函数时,要注意第一个字符串的空间是否足够再容纳第二个字符串,否则会溢出。也即,声明第一个字符串时只能用数组形式,且指定足够大的长度。(注意这个长度应该包含一个空字符’\0’)
void strcat_test()
{
char s1[100] = "In this world, ";
char s2[] = "the car is far away and the horse is slow.";
strcat(s1,s2);
puts(s1);
puts(s2);
}
输出:
In this world, the car is far away and the horse is slow.
the car is far away and the horse is slow.
如果写成char s1[] = "In this world, ";
则会发生缓冲区溢出错误(或许某次运行时不会发生)。
比如:我这次运行把原来存储 s2处的数据破坏了(开头少了一个 t):
In this world, the car is far away and the horse is slow.
he car is far away and the horse is slow.
三、strncat()函数—strcat()的升级
由于前面的strcat()函数可能会造成缓冲区溢出,所以我们可以使用strncat()函数来代替他,该函数新增第3个参数,用来指定最大添加字符数。(同样的,你不要忘记计算空字符了)
示例:
void strncat_test()
{
char s1[20] = "Meet you, ";
char s2[] = "love surge, see everything in the world, are romantic heart.";
strncat(s1, s2, 20-strlen(s1)-1);
puts(s1);
puts(s2);
}
输出:
Meet you, love surg
love surge, see everything in the world, are romantic heart.
四、strcmp()和strncmp()—比较
如何比较下列两个字符串是否相同?
char s1[20] = "Hello, Tom.";
char s2[15] = "Hello, Jay.";
使用:
if(s1 == s2)
这肯定不对,s1、s2是地址,它们的值肯定不同。我们要比较的是字符串内容。
以使用C标准库中的strcmp()函数(用于字符串比较)。该函数通过比较运算符来比较字符串,就像比较数字一样。如果两个字符串参数相同,该函数就返回0,否则返回非零值(真或者说TRUE)。
示例:
void strcmp_test()
{
char s1[20] = "Hello, Tom.";
char s2[15] = "Hello, Jay.";
printf("%d\n",strcmp(s1,s2));
printf("%d\n", strcmp("OK", "OK"));
printf("%d\n",strcmp("A","B"));
printf("%d\n", strcmp("A", "C"));
printf("%d\n", strcmp("B", "A"));
printf("%d\n", strcmp("d", "a"));
}
输出:
1
0
-1
-1
1
1
说明:
- 函数比较的是字符串,不是整个数组,不用管两个数组大小是否相同;
- 从左到右依次比较,直到出现一对不同的字符;
- 输入参数是字符串(双引号),要比较单个字符(单引号)直接判断是否相等就行了;
- 两个字符串相同,返回0;否则返回非0(前者比后者小返回-1,反之返回1,可能与编译器有关,有的返回这两个不同字符的ASCII码的差值)。
strncmp()函数则通过低3个参数,指明只比较两个字符串的前 n 个字符是否相同。
printf("%d\n", strncmp("Fighting", "Fight",5));
比较前5个字符,相同,返回0
。
五、strcpy()和strncpy()—拷贝
前面提到过,如果pts1和pts2都是指向字符串的指针,那么下面语句拷贝的是字符串的地址而不是字符串本身:
pts2 = pts1;
如果希望拷贝整个字符串,要使用strcpy()
函数。
strcpy()将第2个参数指向的字符串被拷贝至第1个参数指向的数组中。拷贝出来的字符串被称为目标字符串,最初的字符串被称为源字符串。
strcpy()接受两个字符串指针作为参数,可以把指向源字符串的第2个指针声明为指针、数组名或字符串常量;而指向源字符串副本的第1个指针应指向一个数据对象(如,数组),且该对象有足够的空间储存源字符串的副本。记住,声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间。
示例(接收一个用户输入,如果开头是I,则拷贝给s1):
void strcpy_test()
{
char s1[20];
char s2[20];
fgets(s2,18,stdin);
//gets_s(s2,18);
if (!strncmp(s2, "I", 1))
strcpy(s1,s2);
puts(s2);
puts(s1);
}
输入输出:
I love you more than i can say.
I love you more t
I love you more t
strcpy()函数还有两个有用的属性。第一,strcpy()的返回类型是char *,该函数返回的是第1个参数的地址。第二,第1个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分。
例:
char s3[20] = "I am your dad";
puts(s3);
printf("%s\n",strcpy(s3+10,"son"));
printf("%s\n", s3);
输出:
I am your dad
son
I am your son
strcpy()和strcat()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。拷贝字符串用strncpy()
更安全,该函数的第3个参数指明可拷贝的最大字符数。
这里就不写例子了。
六、sprintf()函数—合并多个字符串
sprintf()函数声明在stdio.h
中,而不是在string.h中。该函数和printf()类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf()的第1个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表。
示例:
void sprintf_test()
{
char s1[50] = { "Hello, " };
sprintf(s1+strlen(s1),"%sI am %d yesrs old. ","i am Tom. ",18);
puts(s1);
}
输出:
Hello, i am Tom. I am 18 yesrs old.
七、其他可能用到的字符串函数
(1)strstr()函数
原型:
char *strstr(const char *s1,const char *s2);
返回指向s1字符串中s2字符串首次出现的位置,没有则返回空指针。
例,用它来写一个函数,输出字符串中所有出现字母a的下标:
void strstr_test()
{
char *s1 = " The Panda has a big head.";
//printf("%zd\n",strlen(s1)-strlen(strstr(s1,"a")));
int i = 0;
while (*(s1+i) != '\0')
{
if (strstr(s1 + i, "a") != NULL)
{
i = strlen(s1) - strlen(strstr(s1 + i, "a"));
printf("%d\n",i);
}
i++;
}
}
输出:
6
9
12
15
23
(2)strchr()函数
char *strchr(const char * s, int c);
如果s字符串中包含c字符,该函数返回指向s字符串首次出现的c字符的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。
(3)strrchr()函数
char *strrchr(const char * s, int c);
该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。
八、ctype.h中的字符函数
处理单个字符的函数。
有些函数涉及本地化,指的是为适应特定区域的使用习惯修改或扩展C基本用法的工具(例如,许多国家在书写小数点时,用逗号代替点号,于是特殊的本地化可以指定C编译器使用逗号以相同的方式输出浮点数,这样123.45可以显示为123,45)。注意,字符映射函数(如tolower)不会修改原始的参数,这些函数只会返回已修改的值。
单字节 | 宽字节 | 描述 |
---|---|---|
isalnum | iswalnum | 是否为字母数字 |
isalpha | iswalpha | 是否为字母 |
islower | iswlower | 是否为小写字母 |
isupper | iswupper | 是否为大写字母 |
isdigit | iswdigit | 是否为数字 |
isxdigit | iswxdigit | 是否为16进制数字 |
iscntrl | iswcntrl | 是否为控制字符 |
isgraph | iswgraph | 是否为图形字符(例如,空格、控制字符都不是) |
isspace | iswspace | 是否为空格字符(包括制表符、回车符、换行符等) |
isblank | iswblank | 是否为空白字符(C99/C++11新增)(包括水平制表符) |
isprint | iswprint | 是否为可打印字符 |
ispunct | iswpunct | 是否为标点 |
tolower | towlower | 转换为小写 |
toupper | towupper | 转换为大写 |
不适用 | iswctype | 检查一个wchar_t是否是属于指定的分类 |
不适用 | towctrans | 使用指定的变换映射来转换一个wchar_t(实际上是大小写的转换) |
不适用 | wctype | 返回一个宽字符的类别,用于iswctype函数 |
不适用 | wctrans | 返回一个变换映射,用于towctrans |
几种常见的字符集:
- ASCII编码:单字节编码
- latin1编码:单字节编码
- gbk编码:使用一字节和双字节编码,0x00-0x7F范围内是一位,和 ASCII保持一致。双字节的第一字节范围是0x81-0xFE
- UTF-8编码:使用一至四字节编码,0x00–0x7F范围内是一位,和 ASCII 保持一致。其它字符用二至四个字节变长表示。
宽字节就是两个以上的字节。
九、把字符串转换为数字
数字既能以字符串形式储存,也能以数值形式储存。把数字储存为字符串就是储存数字字符。例如,数字213以’2’、‘1’、‘3’、'\0’的形式被储存在字符串数组中。以数值形式储存213,储存的是int类型的值。
C要求用数值形式进行数值运算(如,加法和比较)。但是在屏幕上显示数字则要求字符串形式,因为屏幕显示的是字符。printf()和sprintf()函数,通过%d和其他转换说明,把数字从数值形式转换为字符串形式,scanf()可以把输入字符串转换为数值形式。C还有一些函数专门用于把字符串形式转换成数值形式。
假设你编写的程序需要使用数值命令形参,但是命令形参数被读取为字符串。因此,要使用数值必须先把字符串转换为数字。如果需要整数,可以使用atoi()函数(用于把字母数字转换成整数),该函数接受一个字符串作为参数,返回相应的整数值。
下面的函数都位于stdlib.h
中。
(1)atoi()
- 将字符串形式的数字转为整数;
- 字符串开头是数字,后面是字符,只转换开头的数字;
- 字符串开头不是数字,返回0。
例:
printf("%d %d\n", atoi("123"),"123");
printf("%d\n", atoi("30 days"));
printf("%d\n", atoi("Five 23 days"));
输出:
123 -2111757740
30
0
(2)atof
转成double
(3)atol
转成long
(4)strtol()、strtoul()、strtod()
转成long、unsigned long、double。
其中strtol()和strtoul()可以指定进制。
例:
char number[] = "7f";
printf("%d\n", strtol(number,number+2,16));
printf("%d\n", strtol(number,number+2,10));
输出:
127
7
十、命令行参数
你可能见过两大形式的main()函数:
int void main()
{
...
}
或者:
int void main(argc,**argv)
{
...
}
一般我们用第一种形式的。
一般,我们在IDE中运行代码。
我们的程序写好之后,我们不可能总是打开IDE(如Visual Studio)来运行它,而是生成一个可执行程序,每次运行这个可执行程序即可。
在win或linux 的命令行输入可执行文件的名称即可运行程序(注意路径)。
比如:
在该路径下打开命令行,输入可执行程序名称,即可运行:
如果想要手动输入一下参数怎么办呢?
当然可以用scanf()、fgets()这些函数,上图中就是这么做的。
对应的代码可能是这样的:
int main()
{
char str[20];
puts("Please input s string:");
fgets(str, 18, stdin);
puts(str);
return 0;
}
但使用命令行参数,则更加简单、灵活。
这时,代码可能是这样的:
int main(int argc,char **argv)
{
puts("argv values are:");
for (int i = 0;i < argc;i++)
puts(*(argv+i));
return 0;
}
在命令行运行:
C:\Users\...\Release> dist_test 666 "Hllo csdn." 你好
argv values are:
dist_test
666
Hllo csdn.
你好
如果你写了一个处理文件的程序,用这种方式指定输入输出文件是个不错的选择。
解读:
- C编译器允许main()函数没有或者有2个参数;
- 有2个参数时,第一个为int型的值,称为参数计数,即argc(argument count);第二个为指针数组,用来保存你的输入字符串,即argv(argument value);
- argv的第一个字符串是你的程序的名字;后面则是你在命令行输入的字符串;
- 第一个参数的值等于
1 + 你输入的字符串个数
;- 在命令行输入字符串时,每个字符串的分隔符是空格,如果你输入的一个字符串包含多个单词,用双引号括起来就可以了。
十一、练习—字符串排序
有如下指针数组:
char *p[4] = { "Do your best this time.",
"And i always believe in you.",
"But you have not ",
"What's the matter?" };
排序函数:
void str_sort(char *str[],int n)
{
char *temp;
for (int i = 0;i < n - 1;i++)
{
for (int j = i + 1;j < n;j++)
{
if (strncmp(str[i], str[j],1) > 0) //前比后大为正
{
temp = str[j];
str[j] = str[i];
str[i] = temp;
}
}
}
for (int k = 0;k < n;k++)
puts(str[k]);
}
输出:
And i always believe in you.
But you have not
Do your best this time.
What's the matter?
附录:
本文代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void strlen_test();
void strcat_test();
void strncat_test();
void strcmp_test();
void strcpy_test();
void sprintf_test();
void strstr_test();
void str2num();
void str_sort(char *str[],int n);
int main()
{
char *p[4] = { "Do your best this time.",
"And i always believe in you.",
"But you have not ",
"What's the matter?" };
//strlen_test();
//strcat_test();
//strncat_test();
//strcmp_test();
//strcpy_test();
//sprintf_test();
//strstr_test();
//str2num();
//puts("argv values are:");
//for (int i = 0;i < argc;i++)
//puts(*(argv+i));
str_sort(p,4);
return 0;
}
void strlen_test()
{
char s1[] = "I am dog.";
char* s2 = "I am dog.";
char s3[] = {'H','e','l','l','0',' '};
char s4[20] = "Hello, C.";
printf("%zd\n",strlen(s1));
printf("%zd\n", sizeof(s1));
printf("%zd\n", strlen(s2));
printf("%zd\n", sizeof(s2));
printf("%zd\n", strlen(s3));
printf("%zd\n", sizeof(s3));
printf("%zd\n", strlen(s4));
printf("%zd\n", sizeof(s4));
printf("%zd\n", sizeof(s1)/sizeof(char));
}
void strcat_test()
{
char s1[100] = "In this world, ";
char s2[] = "the car is far away and the horse is slow.";
char* s3 = "In this world, ";
strcat(s1,s2);
puts(s1);
puts(s2);
}
void strncat_test()
{
char s1[20] = "Meet you, ";
char s2[] = "love surge, see everything in the world, are romantic heart.";
strncat(s1, s2, 20-strlen(s1)-1);
puts(s1);
puts(s2);
}
void strcmp_test()
{
char s1[20] = "Hello, Tom.";
char s2[15] = "Hello, Jay.";
printf("%d\n",strcmp(s1,s2));
printf("%d\n", strcmp("OK", "OK"));
printf("%d\n",strcmp("A","B"));
printf("%d\n", strcmp("A", "C"));
printf("%d\n", strcmp("B", "A"));
printf("%d\n", strcmp("d", "a"));
printf("%d\n", strncmp("Fighting", "Fight",5));
}
void strcpy_test()
{
char s1[20];
char s2[20];
char s3[20] = "I am your dad";
//fgets(s2,18,stdin);
//gets_s(s2,18);
if (!strncmp(s2, "I", 1))
strcpy(s1,s2);
//puts(s2);
//puts(s1);
puts(s3);
printf("%s\n",strcpy(s3+10,"son"));
printf("%s\n", s3);
}
void sprintf_test()
{
char s1[50] = { "Hello, " };
sprintf(s1+strlen(s1),"%sI am %d yesrs old. ","i am Tom. ",18);
puts(s1);
}
void strstr_test()
{
char *s1 = " The Panda has a big head.";
//printf("%zd\n",strlen(s1)-strlen(strstr(s1,"a")));
int i = 0;
while (*(s1+i) != '\0')
{
if (strstr(s1 + i, "a") != NULL)
{
i = strlen(s1) - strlen(strstr(s1 + i, "a"));
printf("%d\n",i);
}
i++;
}
}
void str2num()
{
char number[] = "7f";
printf("%d %d\n", atoi("123"),"123");
printf("%d\n", atoi("30 days"));
printf("%d\n", atoi("Five 23 days"));
printf("%zd\n", sizeof(long long));
printf("%d\n", strtol(number,number+2,16));
printf("%d\n", strtol(number, number + 2, 10));
}
void str_sort(char *str[],int n)
{
char *temp;
for (int i = 0;i < n - 1;i++)
{
for (int j = i + 1;j < n;j++)
{
if (strncmp(str[i], str[j],1) > 0) //前比后大为正
{
temp = str[j];
str[j] = str[i];
str[i] = temp;
}
}
}
for (int k = 0;k < n;k++)
puts(str[k]);
}