【C 字符串】02 字符串函数(命令行参数)

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)当字符串用数组形式定义,且根据初始化字符串分配存储空间时:

  1. strlen()函数计算字符串除了 ‘\0’ 字符的长度;
  2. sizeof()函数计算字符串实际占用内存的字节数,包括结尾的 ‘\0’ ;
  3. 如果字符串使用指针形式定义:strlen()函数不变,sizeof()函数计算的是这个指针变量本身的大小(而不是它指向的那个字符串的大小),64位系统是8个字节;
  4. 对于字符数组:两者都可以计算字符数组的元素个数,且相等(因为字符数组没有’\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

说明:

  1. 函数比较的是字符串,不是整个数组,不用管两个数组大小是否相同;
  2. 从左到右依次比较,直到出现一对不同的字符;
  3. 输入参数是字符串(双引号),要比较单个字符(单引号)直接判断是否相等就行了;
  4. 两个字符串相同,返回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()

  1. 将字符串形式的数字转为整数;
  2. 字符串开头是数字,后面是字符,只转换开头的数字;
  3. 字符串开头不是数字,返回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 的命令行输入可执行文件的名称即可运行程序(注意路径)。

比如:
【C 字符串】02 字符串函数(命令行参数)_第1张图片

在该路径下打开命令行,输入可执行程序名称,即可运行:
【C 字符串】02 字符串函数(命令行参数)_第2张图片

如果想要手动输入一下参数怎么办呢?

当然可以用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
#include
#include

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]);
}

你可能感兴趣的:(C语言,c语言,c++,算法)