字符函数和字符串函数

0 阅读9分钟

在编程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数。

1 字符函数

1.1 字符分类函数

C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。这些函数的使用都需要包含一个头文件ctype.h

例如islowerisupper等,以islower为例:

int islower (int c);

islower能够判断参数部分的c是否是小写字母。通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。关于(int c),要注意到字符的本质是一个ASCII码值,这样的写法并没有问题。

1.2 字符转换函数

在之前,要一个字符大小写转换打印,用的是字符+/-32来实现的,例如:大写转小写就加上32、小写转大写就减去32(参见ASCII码相关知识)。现在,我们可以用以下库函数来更简洁的解决。

C语言提供了2个字符转换函数:

int tolower (int c);//将参数传进去的大写字母转小写
int toupper (int c);//将参数传进去的小写字母转大写

举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <ctype.h>
int main()
{
	char i = 'a';
	//i = i - 32;
	i = toupper(i);
	printf("%c\n", i);
	return 0;
}

2 字符串函数

1 strlen的使用和模拟实现

size_t strlen ( const char * str );
  • 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包

'\0')。

  • 参数指向的字符串必须要以'\0'结束。
  • 函数的返回值为size_t,是无符号的(⭐)。
  • strlen的使用需要包含头文件string.h

strlen的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <string.h>
#include <stdio.h>
#include <assert.h>

//模拟实现strlen
size_t my_strlen1(const char* str)
{
	assert(str != NULL);
	size_t n = 0;
	while (*str != '\0')
	{
		n++;
		str++;
	}
	return n;
}

size_t my_strlen2(const char* str)
{
	assert(str != NULL);
	char* p = str;
	while (*p)
	{
		p++;
	}
	return p - str;
}

size_t my_strlen3(const char* str)
{
	assert(str != NULL);
	if (*str)
	{
		return 1 + my_strlen3(str + 1);
	}
	else
	{
		return 0;
	}
}

int main()
{
	char arr[] = { "abcdef" };
	size_t r = strlen(arr);
	size_t r1 = my_strlen1(arr);
	size_t r2 = my_strlen2(arr);
	size_t r3 = my_strlen3(arr);
}

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'拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可修改。

strcpy的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <string.h>
#include <stdio.h>
#include <assert.h>

//模拟实现strcpy
//char* my_strcpy(char* des, const char* src)
//{
//	assert(des != NULL && src != NULL);
//	char* ret = des;
//	while (*src)
//	{
//		*des = *src;
//		des++;
//		src++;
//	}
//	*des = '\0';
//	return ret;
//}

char* my_strcpy(char* des, const char* src)
{
	assert(des != NULL && src != NULL);
	char* ret = des;
	while (*des++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = { "abcdef" };
	char arr2[20] = {"xxxxxxxxxxxxxxxx"};
	my_strcpy(arr2, arr1);
}

3 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'结束。
  • 目标字符串中也得有'\0',否则没办法知道追加从哪里开始。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 将该函数用于字符串自己给自己追加是不合适的,其结果未定义。

strcat模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* des, const char* src)
{
	assert(des && src);
	char* ret = des;
	while (*des != '\0')
	{
		des++;
	}
	while (*des++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char a[20] = { "Hello \0xxxxxxxxxxx" };//目标字符串要有足够位置
	char b[10] = { "world" };
	//strcat(a, b);
	my_strcat(a, b);
	printf("%s\n", a);
}

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.
  • 判断两个字符串的大小:比较两个字符串中对应位置上字符ASCII码值的大小。
  • 标准规定的返回值要求如下:

strcmp模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strcmp1(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return (*str1 - *str2);
}

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

int main()
{
	char a1[] = { "abcdef" };
	char a2[] = { "abq" };
	int r = strcmp(a1, a2);
	int r1 = my_strcmp1(a1, a2);
	int r2 = my_strcmp2(a1, a2);
	return 0;
}

strcpystrcatstrcmp三个函数有对应的更安全的版本(要多传一个参数),它们分别是strncpystrncatstrncmp

5 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个。

6 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.(将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个\0字符)。
  • If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果source指向的字符串的长度小于num的时候,只会将字符串中到\0的内容追加到destination指向的字符串末尾)。

7 strncmp函数

int strncmp ( const char * str1, const char * str2, size_t num );
  • Compares up to num characters of the C string str1 to those of the C string 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, until a terminating null-character is reached, or until num characters match in both strings, whichever happens first.
  • 比较str1str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束。返回值标准参照strcmp函数。

8 strstr的使用和模拟实现

const char * strstr ( const char * str1, const char * str2 );
  • Returns a pointer to the first occurrence of str2 in strl, ora null pointer if str2 is not part of strl.(函数返回字符串str2在字符串str1中第一次出现的位置)。
  • The matching process does not include the terminating null-characters, but it stops there.(字符串的比较匹配不包含\0字符,以\0作为结束标志)。
  • 返回值规定:A pointer to the first occurrence in str1 of the entire sequence of characters specified in str2, or a null pointer if the sequence is not present in str1.

strstr模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* cur = str1;//记录比较的起始位置
	const char* s1 = NULL;//用s1在str1中遍历
	const char* s2 = NULL;//用s2在str2中遍历
	
	//特殊情况:str2是一个空字符串
	if (*str2 == '\0')
		return str1;

	while (*cur)
	{
		s1 = cur;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return (char*)cur;
		cur++;
	}
	return NULL;//找不到
}

int main()
{
	char a[] = { "abbbcdef" };
	char b[] = { "bbc" };
	//char* r = strstr(a, b);
	char* r = my_strstr(a, b);
	printf("%s\n", r);
}

示意图:

str1str2分别记录字符串a、b的起始位置;cur记录比较的起始位置;s1s2用于遍历,完成一次比较后分别靠curstr2复位。

另,字符串匹配算法——KMP算法。

9 strtok函数的使用

char * strtok ( char * str, const char * sep );
  • sep参数指向一个字符串,定义了用作分隔符的字符集合。
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  • strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回NULL指针。

举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>


int main()
{
	char arr[] = "zpw@bitedu.net.hehe";
	char buf[20] = {0};//可以修改
	strcpy(buf, arr);
	//zpw\0bitedu\0net
	const char* p = "@.";
	char* r = NULL;
	for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
	{
		printf("%s\n", r);
	}

	//这样用是不合适的
	//char* r1 = strtok(buf, p);
	//printf("%s\n", r1);//zpw
	//r1 = strtok(NULL, p);
	//printf("%s\n", r1);//bitedu
	//r1 = strtok(NULL, p);
	//printf("%s\n", r1);//net
	return 0;
}

10 strerrorperror函数的使用

strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

char * strerror ( int errnum );

在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在errno.h这个头文件中说明的,C语言程序启动的时候就会使用一个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

除此之外,还有一个函数perror,它不需要传入错误码,会直接将错误信息打印出来。

void perror ( const char * str );

perror函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。

举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//以读的形式打开test.txt文件,如果文件不存在,则打开失败。
	if (pf == NULL)
	{
		//打开文件失败了
		perror("错误信息是");//: xxxx
		printf("错误信息是: %s\n", strerror(errno));
		return 1;
	}
	//往下走了
	//...
	return 0;
}