字符函数和字符串函数

一、字符串函数

注:

  1. 调用下面介绍对字符串操作的函数要包含头文件<string.h>
  2. siez_t即无符号整型,但是不代表使用该类型做返回值类型就更适合,返回类型还是需要根据实际情况而定。
    eg:strlen函数中有些情况就不能使用,该类型来返回

1. str开头的字符串函数

①strlen

函数声明:size_t strlen (const char* str);
作用:计算字符串长度不包括’\0’

函数介绍:

  1. 字符串以’\0’为结束标志,返回值是字符串中’\0’前面出现的字符个数(不包括’\0’)。
  2. 参数 str指向的字符串必须以’\0’结束。

使用:
eg:

#include <stdio.h>
#include <string.h>

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2 > str1\n");
	}
	else
	{
		printf("str1 > str2\n");
	}
	return 0;
}

//输出:str2 > str1

解释输出结果:输出结果并不符合预期
原因:两个无符号数相减的结果还是无符号数,它会把最高位符号位,当作无符号数进行计算。

模拟实现:

// 1. 正常模拟实现
size_t my_strlen(const char *str) 
{
	// 使用assert函数检测传进来的指针
	// assert包含在头文件<assert.h>中
	assert(str);            
    size_t length = 0;  
    while (*str != '\0') 
    {  
        length++;  
        str++;  
    }  
    return length;  
} 


// 2. 递归的方式模拟实现
size_t my_strlen(const char* str)
{
    assert(str);
    if (*str != '\0')
    {
        return 1 + my_strlen(str + 1);
    }
    else
        return 0;
}

②strcpy

函数声明:char* strcpy(char* destination, const char* source);
作用:字符串拷贝

函数介绍:

  1. 源字符串必须以 ‘\0’ 结束
  2. 会将源字符串的’\0’拷贝到目标空间。
  3. 保证目标空间足够大,以确保可以存放源字符串。

注:长度不受限制的字符串操作函数,到’\0’才停止,不安全

模拟实现:


char* my_strcpy(char* dest, const char* src) 
{
    assert(dest && src);  
    char* ret = dest;       // 保存dest的原始值,以便返回  
    while (*src != '\0') 
    {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';  
    return ret;
}

③strcat

函数声明:char* strcat(char* destination, const char* source);
作用:追加字符串

函数介绍:

  1. 源字符串和目标空间都要有’\0’。(原因:要先寻找到目标空间的’\0’才能进行追加。)
  2. 目标空间要足够大
  3. 不能自己给自己追加字符串。(原因:会导致死循环,在追加第一个字符的同时,源字符串的’\0’就被改变了。

注:长度不受限制的字符串操作函数,到’\0’才停止,不安全

模拟实现:

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

④strcmp

函数声明:int strcmp (const char* str1, const char* str2)
作用:字符串比较

规定:

  1. str1 > str2 返回值是一个大于0的数字。
  2. str1 < str2 返回值是一个小于0的数字。
  3. str1 = str2 返回值是0。

注:长度不受限制的字符串操作函数,到’\0’才停止,不安全

模拟实现:

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

⑤strncpy

函数声明:char* strncpy(char* destination, const char* source, size_t num);
作用:拷贝num个字符从源字符串到目标空间。

函数介绍:

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

⑥strncat

函数声明:char* strncat( char* destination, const char* source, size_t num)
作用:追加字符串

⑦strncmp

函数声明:int strncmp (const char* str1, const char* str2, size_t num)

规定:比较到出现另个字符不一样或者一个字符串结束或者num个字符比较完。

注意:

  1. string1 substring less than string2 substring 返回值小于0.
  2. string1 substring identical to string2 substring 返回值为0.
  3. string1 substring greater than string2 substring 返回值大于0.

⑧strstr

函数声明:char* strstr (const char* str1, const char* str2);
作用:查找一个字符串是否在另一个字符串之中

函数介绍:

  1. 如果str2在str1中,返回当前位置的指针,否则返回NULL;

模拟实现:

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* cp = (char*)str1;
	char* s1, * s2;
	
	// 如果 str2 是一个空字符串,则返回 str1 的指针
	if (!*str2)
		return((char*)str1);

	while (*cp)
	{
		s1 = cp;
		s2 = (char*)str2;

		// 比较从 cp 开始的 str1 的子字符串和 str2。
		// 如果两个字符串匹配,s2 会到达其结尾的 \0
		while (*s1 && *s2 && !(*s1 - *s2))
			s1++, s2++;

		// s2到达了其结尾的 \0,cp的位置就是字串str2在str1的位置
		if (!*s2)
			return cp;
		cp++;
	}
	return NULL;
}

⑨strtok

函数声明:char* strtok (char* str, const char* sep);
作用:切割字符串

函数声明:

  1. str:被切割字符串地址,sep:分隔符集合的字符串地址(里面存放的都是分隔符)。
  2. strtok函数找到str中的下一个标记,并且用’\0’结尾,返回一个指向这个标记的指针。(注意:在操作切割字符串的同时最好拷贝到一个新的空间,再进行切割)
  3. strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  4. strtok函数的第一个参数为NULL,函数将在上一个被保存的位置,查找下一个标记。
  5. 如果字符串到结尾,则返回NULL;

使用:
eg:

#include <stdio.h>
int main()
{
	const char* p = "zhangpengwei@bitedu.tech";
	const char* sep = ".@";   // '\0'不能当分隔符,这是字符串结束标志
	char arr[30];
	char* str = NULL;
	strcpy(arr, p);

	// str = strtok(NULL, sep) 这里strtok会记住上一个标志,所以使用NULL即可
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	return 0;
}

//输出
//zhangpengwei
//bitedu
//tech

⑩strerror

char* strerror (int errnum)
返回错误码所对应的信息。

函数介绍:

  1. strerror函数要和errno函数连用
  2. perror函数,这个函数相当于strerror函数和errno和printf函数联合使用。如果不需要打印错误信息,将strerror和errno联合使用就行

使用:
eg:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	FILE* pFile = fopen("test.txt", "r");
	//这里涉及文件操作的知识
	if (NULL == pFile)
	{
		printf("Error opening file test.txt:%s\n", strerror(errno));
		//使用erron需要包含头文件<errno.h>
	}
	fclose(pFile);
	pFile = NULL;
	return 0;
}

2. mem开头的内存操作函数

  1. mem开头的内存操作函数也是进行字符串处理的函数
  2. 当然也不仅仅知识处理字符串,可以作用在所有类型上的内存操作函数

①memcpy

函数声明:void* memcpy (void* destination, const void* source, size_t num);

函数介绍:

  1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  2. 如果source和destination有任何重叠,复制的结果都是未定义的。

注:

  • 这里我们可以注意它的返回类型和第一个、第二个参数类型,都是void*。因为函数不知道我们复制什么类型的,所以留给程序员操作。
  • 该函数遇到’\0’的时候是不会停下来的。

使用:
eg:

#include <stdio.h>
#include <string.h>

struct
{
	char name[40];
	int age;
}person,person_copy;

int main()
{
	char myname[] = "Pierre de Fermat";
	//使用memcpy
	memcpy(person.name, myname, strlen(myname) + 1);
	person.age = 40;
	//使用memcpy复制结构体
	memcpy(&person_copy, &person, sizeof(person));
	printf("person_copy:%s,%d\n", person.name, person.age);
	return 0;
}
//输出: person_copy:Pierre de Fermat,40

模拟实现:

void* my_memcpy(void* dst, const void* src, size_t count)
{
	assert(dst && src);
	void* ret = dst;    // 保存dst的原始值,以便返回  

	while (count--) 
	{
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}
	return ret;
}

②memmove

函数声明:void* memmove (void* destination, const void* source, size_t num);
作用:memmove和memcpy作用一致,唯一区别就是memcpy函数源内存块和目标内存块是不可以重叠的,memmove函数可以。

使用:
eg:

#include <stdio.h>
#include <string.h>

int main()
{
	char str[] = "memmove can be very useful......";
	memmove(str + 20, str + 15, 11);
	puts(str);
	return 0;
}
//output: memmove can be very very useful.

模拟实现:

void* my_memmove(void* dst, const void* src, size_t count)
{
	assert(dst && src);
	void* ret = dst;   // 保存dst的原始值,以便返回  

	char* dst_char = (char*)dst;
	char* src_char = (char*)src;


	if ((dst < src && dst_char + count > src) || (src < dst && src_char + count > dst)) {
		// 需要反向复制以避免覆盖  
		dst = dst_char + count - 1;
		src = src_char + count - 1;
		while (count--) 
		{
			*dst_char = *src_char;
			dst = dst_char - 1;
			src = src_char - 1;
		}
	}
	else {
		// 非重叠或dst在src之前,可以正向复制  
		while (count--) {
			*dst_char = *src_char;
			dst = dst_char + 1;
			src = src_char + 1;
		}
	}
	
	return ret;
}

③memcmp

函数声明:int memcmp( const void *buf1, const void *buf2, size_t count );
作用:比较buf1和buf2指针开始的count个字节

规定:

  1. buf1 < buf2 返回值小于0
  2. buf1 = buf2 返回值0
  3. buf1 > buf2 返回值大于0

使用:
eg:

#include <stdio.h>
#include <string.h>

int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";

	int n;

	n = memcmp(buffer1, buffer2, sizeof(buffer1));

	if (n > 0) 
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0) 
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else 
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);

	return 0;
}

④memset

函数声明:void *memset( void *destination, int c, size_t count );
作用:用c初始化内存区域

函数介绍:

  1. destination:指向要设置的内存区域的指针。
  2. c:要设置的值。这个值被转换为 unsigned char,然后复制到内存区域。
  3. count:要设置的字节数。
    memset 函数将 destination 指向的内存区域的前 count 个字节设置为值 c。返回值是一个指向 destination 的指针。

使用:
eg:

#include <string.h>
#include <stdio.h>

int main()
{
	char buffer[] = "This is a test of the memset function";

	printf("Before: %s\n", buffer);
	memset(buffer, '*', 4);
	printf("After:  %s\n", buffer);
}
//输出:
//Before: This is a test of the memset function
//After : **** is a test of the memset function

模拟实现:
eg:

#include <stddef.h> // 包含 size_t 类型定义  
  
void *my_memset(void *s, int c, size_t n) 
{  
	// 将 void* 转换为 unsigned char* 以进行字节操作
    unsigned char *p = (unsigned char *)s;  
    
    // 确保 c 是 unsigned char 类型,以避免符号扩展 
    unsigned char value = (unsigned char)c;   
    size_t i;  
  
    for (i = 0; i < n; i++) {  
        p[i] = value; // 设置每个字节的值为 value  
    }  
  
    return s; // 返回原始指针  
}

二、字符函数

注:

  1. 调用下面介绍对字符操作的函数要包含头文件<ctype.h>
  2. 0x : 十六进制前缀,本文ASCII码表范围都是用十六进制表示
  3. 下列的所有字符操作函数满足为真的范围,文字描述其作用范围,再描述其在ASCII码表中的范围

如果对字符函数会使用,指向看满足条件的范围,可以看第3点小结部分

1. 字符分类函数

①iscntrl

函数声明:int iscntrl( int c );

函数介绍:

作用范围:任何控制字符
ASCII码表:(0x00 ~ 0x1F or 0x7F)。
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main ()
{
  int i=0;
  char str[]="first line \n second line \n";

  //遇到任何控制字符直接跳出循环
  while (!iscntrl(str[i]))
  {
    putchar (str[i]);
    i++;
  }
  return 0;
}
//输出:first line

②isspace

函数声明:int isspace( int c );

函数介绍:

作用范围:空白字符
ASCII码表:(0x09 ~ 0x0D or 0x20)。也就是 空格’ ‘,换页’\f’,换行’\n’,回车’\r’,制表符’\t’,垂直制表符’\v’。
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "first line \n second line \n";

    //遇到任何形式空白字符直接跳出循环
    while (!isspace(str[i]))
    {
        putchar(str[i]);
        i++;
    }
    return 0;
}
//output:first

③isdigit

函数声明:int isdigit( int c );

函数介绍:

作用范围:十进制数字0~9
ASCII码表:(0x30 ~ 0x39)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    char str[] = "1776ad";
    int year;
    if (isdigit(str[0]))//1 满足条件 可以尝试下标4,5是不满足条件的,会执行else语句
    {
        printf("str[0]是数字:%d\n", (str[0]-'0'));
    }
    else
    {
        printf("str[0]不是数字\n");
    }
    return 0;
}
//str[0]是数字:1

④isxdigit

函数声明:int isxdigit ( int c );

函数介绍:

作用范围:十六进制数字。包括所有十进制数字和字母a~f(字母大小写都行)。
ASCII码表:(0x30 ~ 0x39)or(0x41 ~ 0x46)or(0x61 ~ 0x66)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    char str[] = "1776ad";
    int year;
    if (isxdigit(str[4]))  //a满足条件
    {
        printf("str[4]是十六进制数字:%c\n", str[4]);
    }
    else
    {
        printf("str[4]不是十六进制数字\n");
    }
    return 0;
}
//str[4]是十六进制数字:a

⑤islower

函数声明:int islower ( int c );

函数介绍:

作用范围:小写字母a ~ z
ASCII码表:(0x61 ~ 0x7A)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

//把小写字母转换成大写字母
#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "Test String.";
    char c;
    while (str[i])
    {
        c = str[i];
        if (islower(c))
        {
            //字符转换函数,后文有介绍
            c = toupper(c);
        }
        
        putchar(c);
        i++;
    }
    return 0;
}
//output:TEST STRING.

⑥isupper

函数声明:int isupper ( int c );

函数介绍:

作用范围:大写字母A ~ Z
ASCII码表:(0x41 ~ 0x4A)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

//大写字母转换成小写字母
#include <stdio.h>
#include <ctype.h>
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;
}
//output:test string.

⑦isalpha

函数声明:int isalpha ( int c );

函数介绍:

作用范围:字母a ~ z或A ~ Z
ASCII码表(0x41 ~ 0x4A)or(0x61 ~ 0x7A)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "C++";
    while (str[i])
    {
        if (isalpha(str[i])) 
            printf("%c is alphabetic\n", str[i]);
        else 
            printf("%c is not alphabetic\n", str[i]);
        i++;
    }
    return 0;
}
//output:
//C is alphabetic
//+ is not alphabetic
//+ is not alphabetic

⑧isalnum

函数声明:int isalnum ( int c );

函数介绍:

作用范围:字母或者数字,a ~ z, A ~ Z, 0 ~ 9
ASCII码表:(0x41 ~ 0x4A)or(0x61 ~ 0x7A)or(0x30 ~ 0x39)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
	char str[] = "...c3po";
	//判断数组第一个字符是不是字母或者数字
	if (isalnum(str[0]))
	{
		printf("Yes\n");
	}
	else
	{
		printf("No\n");
	}
	return 0;
}
//output:No

⑨ispunct

函数声明:int ispunct ( int c );

函数介绍:

作用范围:标点符号,任何不属于数字或者字母的图形字符(可打印)
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    int cx = 0;
    char str[] = "Hello, welcome!";
    while (str[i])
    {
        if (ispunct(str[i]))
        {
            //用cx统计该数组的标点元素个数
            cx++;
        }
        i++;
    }
    printf("Sentence contains %d punctuation characters.\n", cx);
    return 0;
}
//output:Sentence contains 2 punctuation characters. 
//数组有两个标点符号字符(,!)

⑩isgraph

函数声明:int isgraph ( int c );

函数介绍:

作用范围:任何图形字符
c语言全部图形字符(这里用ASCII表示范围不太好,所以直接,把所有图形字符放到下面)

! " # $ % & ’ ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~

如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
	char str[] = "3m ";
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		if (isgraph(str[i]))
		{
			printf("YES:%c\n", str[i]);
		}
		else
		{
			break;
		}
	}
	printf("NO:%c\n", str[i]);
	return 0;
}
//output:
//YES:3
//YES:m
//NO :

isprint

函数声明:int isprint ( int c );

函数介绍:

作用范围:任何可打印字符,包括图形字符和空白字符
水平制表符不可以
如果参数c属于该范围,则符合条件返回真(非0)

例子:

#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "first line \t second line \t";
    //遇到不能打印的字符例如'\t',会直接跳过循环,因为循环条件为假
    while (isprint(str[i]))
    {
        putchar(str[i]);
        i++;
    }
    return 0;
}
//output:first line

2. 字符转换函数

①tolower

函数声明:int tolower ( int c );

函数介绍:

大写转换为小写的字母
如果 c 有相对应的小写字母,则该函数返回 c 的小写字母,否则 c 保持不变。

例子:上文介绍isupper也说过这个例子

//转换小写字母
#include <stdio.h>
#include <ctype.h>
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;
}
//output:test string.

②toupper

函数声明:int toupper ( int c );

函数介绍:

把小写字母转换成大写字母
如果 c 有相对应的大写字母,则该函数返回 c 的大写字母,否则 c 保持不变

例子:例子:上文介绍islower也说过这个例子

//把小写字母转换成大写字母
#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "Test String.";
    char c;
    while (str[i])
    {
        c = str[i];
        if (islower(c))
        {
            //字符转换函数,后文有介绍
            c = toupper(c);
        }
        
        putchar(c);
        i++;
    }
    return 0;
}
//output:TEST STRING.

3. 小结

字符分类函数:
字符分类函数

三、其它相关函数atoi模拟实现

函数声明:int atoi (const char * str);
作用:将字符串转换为int类型
atoi库函数定义在头文件<stdlib.h>

模拟实现:

#include <ctype.h>  
#include <limits.h>  

int my_atoi(const char* str) {
    int sign = 1, base = 0;
    
    // 跳过空白字符
    while (isspace(*str)) 
    {   
        str++;
    }

    // 检查转换后的数字是不是负数,看第一个不为空的字符为不为‘-’
    if (*str == '-') 
    {  
        sign = -1;
        str++;
    }
    else if (*str == '+') 
    {
        // 这里也要执行以下,如果第一个字符为‘+’需要跳过
        str++;
    }

    // 转换操作
    while (isdigit(*str)) 
    {   
        if (base > INT_MAX / 10 || (base == INT_MAX / 10 && *str - '0' > 7)) 
        {
            // 处理溢出情况,当结果可能大于INT_MAX时返回INT_MAX或INT_MIN  
            return (sign == 1) ? INT_MAX : INT_MIN;
        }
        base = base * 10 + (*str - '0');
        str++;
    }
    return sign * base;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kpl_20

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值