C语言进阶篇 第三讲【字符串函数的使用和剖析】

一、本章重点

前言:

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中 或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数。

求字符串长度

  strlen

长度不受限制的字符串函数

  strcpy

  strcat

  strcmp

长度受限制的字符串函数介绍

  strncpy

  strncat

  strncmp

字符串查找

  strstr

  strtok

错误信息报告

  strerror

字符操作

二、求字符串长度

  strlen

size_t strlen ( const char * str );

注:

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。

参数指向的字符串必须要以 '\0' 结束。

注意函数的返回值为size_t,是无符号的( 易错 )

使用实例:

#include 
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;
}

这里显然,str1的长度比str2的长度要大,但是,

打印结果竟然是:

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第1张图片

为什么?

我们发现,C库里的strlen函数,返回类型是size_t,

转到源码,这个size_t其实是由unsigned int进行typedef后的新名称,

也就是说,strlen的返回类型是无符号整数。

再看原题,

字符串2减去字符串1等于-3,而对应的无符号整数肯定是一个正数,

所以最终会输出字符串2更大。

模拟实现:

我们有三种方法来实现:

1.计数器的方法

int my_strlen(const char* str)
{
	int count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

2.递归

3.指针-指针

三、长度不受限制的字符串函数

  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* my_strcpy(char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src != NULL);
	char* ret = dest;
	//拷贝src指向的字符串到dest指向的空间,包含'\0'
	while (*dest++ = *src++)
	{
		;
	}
	//返回目的空间的起始地址
	return ret;
}

  strcat--字符串追加

char * strcat ( char * destination, const char * source );

注:

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.

源字符串必须以 '\0' 结束。

目标空间必须有足够的大,能容纳下源字符串的内容。不然会造成越界访问。

----比如字符数组写:char arr[100]

目标空间必须可修改。

那么追加的字符串有没有把'\0'追加过去呢?

我们用下面的方法即可判断:

arr2从\0处开始追加,

只需要看第五个x有没有被改成\0。 

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第2张图片

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第3张图片

答案是追加了。

 

字符串自己给自己追加,如何?

会崩溃。不能自己给自己加。

 

模拟实现:

1.找到目的字符串的'\0'

2.追加

char* my_strcat(char* dest,const char* src)
{
	assert(dest != NULL && src != NULL);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;

}

  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的数字

在vs里,只会返回-1,0,1三个数字。

在LINUX下,返回的是差值。

所以判断不能用0,-1和1来判断。

那么如何判断两个字符串?判断的是长度吗?

并不是。

我们来比较一下:

p1:“abcdef”

p2:"qwer"

strcmp(p1,p2);

这里的结果是-1.

说明比较的并不是长度,

而是第一个字符的ASCII码值。

这里第一个字符a的ASCII比q要小,

所以返回-1.

那么如果是:

“abcdef"

"awer"

呢?

既然第一对都是a,

那么转而比较下一对,也就是b和w。

b比w小,所以答案还是-1.

模拟实现:

int my_strcmp(const char* str1,const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return (*str1 - *str2);
}

四、长度受限制的字符串函数介绍

  strncpy--个数受限制的字符串拷贝

char * strncpy ( char * destination, const char * source, size_t num );

Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

拷贝num个字符从源字符串到目标空间。

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个

 

char arr1[10] = "abcdef";
char arr2[] = "hello bit";
strncpy(arr1,arr2,4);

这样得到的结果是:hellef

也就是说,会把第二个字符串的前四个字符拷贝到第一个字符串的前四个位置中,并且不自动拷贝'\0'。

 但是,如果是这样的代码:

char arr1[10] = "abcdef";
char arr2[] = "hel";
strncpy(arr1,arr2,4);

第二个字符一共只有3个字符,

但要拷贝4个过去,会怎么样?

系统会自动补0,直到一共有4个字符。 

模拟实现:

char* my_strncpy(char* dest, char* src,int count)
{
	assert(dest && src);
	char* ret = dest;
	while (count && (*dest++ = *src++))
	{
		count--;
	}
	if (count)
	{
		while (--count)
		{
			*dest++ = '\0';
		}
	}
	return ret;
}

  strncat--个数受限制的字符串追加

char * strncat ( char * destination, const char * source, size_t num );

Appends the first num characters of source to destination, plus a terminating null-character.

If the length of the C string in source is less than num, only the content up to the terminating nullcharacter is copied.

/* strncat example */
#include 
#include 
int main ()
{
 char str1[20];
 char str2[20];
 strcpy (str1,"To be ");
 strcpy (str2,"or not to be");
 strncat (str1, str2, 6);
 puts (str1);
 return 0;
}

这里介绍一下puts函数:

这个函数也很简单,只有一个参数。s可以是字符指针变量名、字符数组名,或者直接是一个字符串常量。功能是将字符串输出到屏幕。输出时只有遇到 '\0' 也就是字符串结束标志符才会停止。

# include 
int main(void)
{
    char name[] = "祖国!";
    printf("%s\n", name);  //用printf输出
    puts(name);  //用puts()输出
    puts("我爱你!");  //直接输出字符串
    return 0;
}

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第4张图片 

可见使用 puts() 输出更简洁、更方便。而且使用 puts() 函数连换行符 '\n' 都省了,使用 puts() 显示字符串时,系统会自动在其后添加一个换行符。

也就是说:

  1. printf("%s\n", name);
  2. puts(string);

等价; 

  strncmp个数受限制的字符串比较

int strncmp ( const char * str1, const char * str2, size_t num );

比较到以下三种情况:

出现另个字符不一样

或者一个字符串结束

或者num个字符全部比较完。

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第5张图片

/* strncmp example */
#include 
#include 
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;
}

 

五、字符串查找

  strstr--字符串查找

char * strstr ( const char *, const char * );

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.

查找目标字符串里有没有源字符串,有的话就返回源字符串的地址。

	char p1[] = "abcdef";
	char p2[] = "defh";
	char* ret = strstr(p1, p2);
	if (ret == NULL)
	{
		puts("字符串不存在");
	}
	else
	{
		puts(ret);
	}

注意,返回的是首元素地址!

模拟实现:

char* my_strstr(char* dest,char* src)
{
	assert(dest && src);
	char* s1 = dest;
	char* s2 = src;
	char* cur = dest;
	if (*src == '\0')
	{
		return dest;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = src;
		while ((* s1 != '\0')&&(*s2 !='\0')&&(*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;	
}

  strtok--字符串分割(会破坏原串)

char * strtok ( char * str, const char * sep );

1.sep参数是个字符串,定义了用作分隔符的字符集合。

2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。

6.如果字符串中不存在更多的标记,则返回 NULL 指针。

 

典例:使用for循环。

#include 
int main()
{
   char *p = "[email protected]";
 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);
 }
}

六、错误信息报告

  strerror

char* strerror ( int errnum );

返回错误码所对应的错误信息。

/* strerror example : error list */
#include 
#include 
#include //必须包含的头文件
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;
}
Edit & Run

0,1,2,3...是错误码,返回的是对应的错误信息。 

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第6张图片 

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第7张图片

 errno是一个全局的错误码变量,

当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码复制到errno中。

七、字符操作

C语言进阶篇 第三讲【字符串函数的使用和剖析】_第8张图片

字符转换函数

int tolower (int c);//转小写字母

int toupper (int c);//转大写字母

/* isupper example */
#include 
#include 
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;
} 

 

你可能感兴趣的:(C语言)