【str家族】如何使用处理字符和字符串的库函数
本文收录于专栏:C语言进阶
关注作者,持续阅读作者的文章,学习更多知识!
https://blog.csdn.net/weixin_53306029?spm=1001.2014.3001.5343
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串 中或者 字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数.
但比较友好的是C语言有处理字符和字符串的一些库函数,方便我们对字符和字符串进行一些操作。
所以本节我会重点介绍处理字符和字符串的一些常用库函数,让大家学会使用,并且了解它们的实现原理和一些注意事项。
str家族
- strlen - 求字符串长度
- 1.函数介绍
- 2.strlen模拟实现(三种方式)
- 1.计数器
- 2.递归
- 3.指针
- strcpy - 字符串拷贝
- 1.函数介绍
- 2.strcpy模拟实现
- strcat - 字符串追加
- 1.函数介绍
- 2.strcat模拟实现
- strcmp - 字符串比较
- 1.函数介绍
- 2. strcmp模拟实现
- strncpy、strncat、strncmp - 限制操作长度
- 1.strncpy
- 2.strncat
- 3.strncmp
- strstr - 字符串查找
- 1.函数介绍
- 2.strstr模拟实现
- strtok - 字符串分割
- strerror、perror - 错误报告函数
- 1.strerror
- 2.perror
- – the End –
strlen - 求字符串长度
1.函数介绍
size_t strlen( const char *string );
strlen函数是一个用于求字符串长度的库函数。它的参数是被求长度的字符串的起始地址,返回值是一个无符号整型。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "helloworld";size_t ret = strlen(arr);return 0;
}
我们定义一个size_t型的变量ret接收函数的返回值,即字符串长度,结果是10
注意
- 1.参数指向的字符串要以’\0’结束。
- 2.strlen返回的是在字符串中’\0’之前出现的字符个数(不包含’\0’)。
int main()
{//char arr[] = "abc";//'c'后面是'\0',所以字符串长度是3char arr[] = { 'a','b','c' };//'c'后面不一定是'\0',所以算出的字符串长度是随机值size_t len = strlen(arr);printf("%d\n", len);return 0;
}
运行结果:
- 3.注意函数的返回值为size_t,是无符号的(易错)。
:下面运行结果应该是什么?
if (strlen("abc") - strlen("abcdef") > 0){printf(">\n");}else{printf("<=\n");}return 0;
答案是大于
strlen接收的是无符号整形,两个无符号整数相减在算数层面还是无符号整数,原本的-3不算符号位,即补码当作原码,会变成一个很大的整数。
很显然,如果我们事先不知道strlen函数的返回值为size_t
,很容易认为结果是<=
。
2.strlen模拟实现(三种方式)
1.计数器
我们定义一个变量为count,如果传入的指针指向的内容不是’\0’,那么count++,同时指针后移一位,循环往复,直到找到’\0’时返回count即可。
size_t my_strlen1(const char* str)
{size_t count = 0;//计数器while (*str){count++;str++;}return count;
}
2.递归
我们一进入函数体就判断传入指针指向的内容是否为’\0’,如果是就返回0,不是就返回1+my_strlen2(str+1),如此进行下去,直到递归到字符串结尾找到’\0’,这时再一步步将值返回回来即可。
size_t my_strlen2(const char* str)
{if (*str == '\0')return 0;elsereturn 1 + my_strlen2(str + 1);
}
3.指针
进入函数体时,我们事先定义一个指针变量将传入的指针保存下来,然后将传入的指针向后移,直到遇到’\0’时,我们返回当前指针与保存的起始位置指针的差值,即是字符串长度。(指针与指针的差的绝对值是两个指针之间的元素个数)
size_t my_strlen3(const char* str)
{const char* p = str;//保存起始位置while (*str != '\0')str++;return str - p;
}
strcpy - 字符串拷贝
1.函数介绍
char *strcpy( char *Destination, const char *Source );
strcpy函数是一个用于拷贝字符串的函数,即将一个字符串中的内容拷贝到另一个字符串中(会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是拷贝字符串的目的地的起始位置,即要将字符串拷贝到什么地方;第二个指向的是要拷贝字符串的内容的起始位置,即需要拷贝的字符串。它的返回值是目标空间的起始位置。
注意:
- 源字符串(需要被拷贝的字符串)必须以’\0’结束。
- 会将源字符串中的’\0’一同拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
举例:
int main()
{char arr[20] = { 0 };//arr = "hello";//err char arr1[10] = "abcaaa";char arr2[] = "def";strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}
运行结果是def
深入来看,我们通过监视看到:
完成拷贝后,arr1只被覆盖了"abca"前四个字符,即拷贝了arr2中的"def"和’\0’,但是输出时默认遇到’\0’结束,所以打印结果只显示"def"三个字符
2.strcpy模拟实现
进入函数体时先定义一个指针变量保存目标空间的起始位置,便于之后返回。然后将源字符串中的字符一一赋值给目标空间,直到遇到源字符串中的’\0’,将’\0’也赋值给目标空间后结束赋值,最后返回目标空间的起始位置。
char* my_strcpy(char* dest, char* src)
{char* ret = dest;//保存目标空间的起始位置assert(dest != NULL);//断言,dest为空指针时报错assert(src != NULL);//断言,src为空指针时报错while (*dest++ = *src++){;}return ret;
}
strcat - 字符串追加
1.函数介绍
char *strcat( char *Destination, const char *Source );
strcat函数是一个用于追加字符串的函数,即将一个字符串中的内容追加到另一个字符串后面(不会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是追加字符串的目的地的起始位置,即要将字符串追加到哪个字符串后面;第二个指向的是要追加字符串的内容的起始位置,即需要追加的字符串。它的返回值是目标空间的起始位置。
注意:
- 源字符串必须以’\0’结束。
- 目标空间必须足够大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 字符串不能给自己追加(’\0’被覆盖,无终止条件)。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world!";strcat(arr1, arr2);return 0;
}
追加结束后arr1数组中的内容变成"hello world!"。
2.strcat模拟实现
进入函数体依然先定义一个指针变量用于存放目标空间的起始位置,便于之后返回。然后用循环先找到目标空间的’\0’,之后从’\0’的位置开始追加源字符串的内容,直到追加到源字符串中的’\0’为止。最后返回目标空间的起始位置。
char* my_strcat(char* dest, const char* src)
{assert(dest != NULL);//断言,dest为空指针时报错assert(src != NULL);//断言,src为空指针时报错char* ret = dest;//找到目标空间的'\0'while (*dest){dest++;}//追加while (*dest++ = *src++){;}return ret;
}
strcmp - 字符串比较
1.函数介绍
int strcmp( const char *string1, const char *string2 );
strcmp函数是一个用于比较两个字符串内容的函数。它的参数是两个指针,指针分别指向两个待比较字符串的起始位置。它的返回值是一个整型数字。当string1大于string2的时候返回一个大于0的数;当string1等于string2的时候返回0;当string1小于string2的时候返回一个小于0的数。
注意:
- 字符串比较的不是字符串长度的大小,而是两个字符串中对应位置字符的ASCII值。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello world!";char arr2[20] = "hello bitt!";int ret = strcmp(arr1, arr2);return 0;
}
比较字符串的时候发现前面字符的ASCII值都相同,直到比较到字符’w’和字符’b’时,发现字符’w’的ASCII值大于字符’b’的ASCII值,于是返回一个大于0的数。
2. strcmp模拟实现
进入函数体直接比较起始位置的字符的大小。如果相同并且不为’\0’那么继续比较下一对字符的大小;如果相同并且为’\0’那么说明字符串比较完毕,那么直接返回0;如果不同则直接返回str1与str2中对应字符的ASCII值的差值(当str1中对应字符大于str2中的对应字符时返回正值,当str1中对应字符小于str2中的对应字符时返回负值)。
int my_strcmp(const char* str1, const char* str2)
{assert(str1 != NULL);//断言,str1为空指针时报错assert(str2 != NULL);//断言,str2为空指针时报错while (*str1 == *str2){if (*str1 == '\0')//字符串全部比较完毕return 0;str1++;str2++;}return *str1 - *str2;
}
strncpy、strncat、strncmp - 限制操作长度
我们发现strcpy是将一个字符串全部拷贝到另一个字符串,strcat是将一个字符串全部追加到另一个字符串后面,strcmp也是比较两个字符串的全部内容,这类操作函数称为长度不受限制的字符串操作函数。
那么我们如果操作字符串时并不想操作整个字符串,而只想操作字符串的一部分怎么办呢?
库函数中的strncpy、strncat、strncmp便解决了这个问题。
1.strncpy
char *strncpy( char *Dest, const char *Source, size_t count );
strncpy的参数与strcpy相比较多出了一个参数,而这个参数就是需要被操作的字符个数。
注意:
- 当操作数小于等于源字符串中的字符个数时,操作数的大小决定被拷贝的字符个数。
- 操作数大于源字符串中字符的个数时,strncpy函数将源字符串中的字符拷贝到目标空间后不够的将用’\0’填充。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[10] = "##########";char arr2[] = "abcd";strncpy(arr1, arr2, 3);strncpy(arr1, arr2, 6);return 0;
}
当操作数为3时,拷贝结束后arr1数组中存放的是"abc#######"
当操作数为6时,拷贝结束后arr1数组中存放的是"abcd\0\0####"
2.strncat
char *strncat( char *Dest, const char *Source, size_t count );
strncat的参数与strcat相比较也多出了一个参数,而这个参数也就是需要被操作的字符个数。
注意:
- 当操作数小于源字符串中的字符个数时,操作数的大小决定被追加的字符个数,并在追加完后再追加一个’\0’。
- 当操作数大于等于源字符串中的字符个数时,将源字符串内容全部追加到目标空间便结束追加,并在追加完后再追加一个’\0’表示结束。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[10] = "abc\0#####";char arr2[] = "def";strncat(arr1, arr2, 2);strncat(arr1, arr2, 5);return 0;
}
当操作数为2时,拷贝结束后arr1数组中存放的是"abcde\0###"
注意:从监视我们可以看出,这里所谓的追加其实是在要追加的目标字符串遇到’\0’的位置开始进行覆盖,除去覆盖位置arr1原本存在的字符并不会显示,只是因为输出时一般默认遇到’\0’结束,所以才达到所谓字符串追加的效果。
当操作数为5时,拷贝结束后arr1数组中存放的是"abcdef\0##"
3.strncmp
int strncmp( const char *string1, const char *string2, size_t count );
strncmp的参数与strcmp相比较也多出了一个参数,而这个参数也就是需要比较的字符个数。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcde";char arr2[] = "abcdf";int ret1 = strncmp(arr1, arr2, 4);int ret2 = strncmp(arr1, arr2, 5);return 0;
}
- 当操作数为4时,我们只比较了arr1和arr2的前4个字符,而它们前4个字符都相同,所以返回的是0;
- 而当操作数为5的时候,我们比较了arr1和arr2的前5个字符,因为字符’e’的ASCII值小于字符’f’的ASCII值,所以返回一个负值。
strstr - 字符串查找
1.函数介绍
char *strstr( const char *string, const char *strCharSet );
strstr函数可以在一个字符串(字符串1)中查找另一个字符串(字符串2),如果字符串2存在于该字符串1中,那么就返回字符串2在字符串1中第一次出现的起始位置,如果在字符串1中找不到字符串2,那么就返回空指针(NULL)。它的第一个参数是字符串1的起始位置,第二个参数是字符串2的起始位置。
注意:
- 若字符串2为空字符串,则返回字符串1的起始位置。
- strstr函数返回的是第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。
举例:在字符串"abcdefbcdef"中查找字符串"cde"
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdefbcdef";char arr2[] = "cde";char* ret = strstr(arr1, arr2);//在arr1中查找arr2字符串第一次出现的位置if (ret != NULL)printf("%s\n", ret);elseprintf("找不到\n");return 0;
}
2.strstr模拟实现
我们需要设置3个指针变量来辅助实现函数功能。
cp指针
: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从cp++处开始下一次的匹配。p1和p2指针
: 通过判断p1和p2指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,p1指针返回cp指针处,p2指针返回待查找字符串的起始位置。
实例:在字符串"abbbcdef"中查找字符串"bbc":
直到当p2指向的内容为\0时,便说明待查找字符串中的字符已经被找完,也说明了从当前cp位置开始匹配能够找到目标字符串,所以此时返回指针cp即可。
代码实现:
char* my_strstr(const char* str1, const char* str2)
{assert(str1 != NULL);//断言,当str1为空指针报错assert(str2 != NULL);//断言,当str2为空指针报错const char* cp = str1;//记录开始匹配时的起始位置if (*str2 == '\0')//要查找的字符串为空字符串return (char*)str1;while (*cp){const char* p1 = cp;const char* p2 = str2;while ((*p1!='\0') && (*p2!='\0') && (*p1 == *p2)){p1++;p2++;}if (*p2 == '\0')//目标字符串已被查找完return (char*)cp;cp++;}return NULL;//找不到目标字符串
}
strtok - 字符串分割
函数原型:
char * strtok (char *str, const char * delimiters);
参数:
str,待分割的字符串;delimiters,分割符字符串。
该函数用来将字符串分割成一个个片段。参数str指向欲分割的字符串,参数delimiters则为分割字符串中包含的所有字符。当strtok()在参数str的字符串中发现参数delimiters中包涵的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数str字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
需要注意的是,使用该函数进行字符串分割时,会破坏被分解字符串的完整,调用前和调用后的s已经不一样了。所以我们可以将待分割的字符串拷贝一份使用,防止原数据被修改。
注意:
- strtok函数找到str中的一个标记时,会将其用 \0结尾(即用’\0’覆盖)并返回这个标记的首地址。
- strtok函数会改变str函数,所以在使用strtok函数切分字符串时应该临时拷贝一份,并且内容可修改。
- strtok函数的第一个参数不为NULL时,函数将找到str中的第一个标记,并保存它在字符串中的位置。
- strtok函数的第一个参数为NULL时,函数将从同一个字符串中被保存的位置开始查找它的下一个标记。
- 若字符串中不存在更多的标记,则返回NULL指针。
举例:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "275207042@qq.com";//待分割字符串char arr2[] = "@.";//分隔符的字符集合char arr3[20] = { 0 };strcpy(arr3, arr1);//将数据拷贝一份使用,防止原数据被修改char* token = strtok(arr3, arr2);//第一次传参需传入待分割字符串首地址while (token != NULL)//说明还未分割完{printf("%s\n", token);token = strtok(NULL, arr2);//第二次及以后的第一个参数设为NULL}return 0;
}
运行结果:
通过 ‘@
’ 和 ‘.
’ 将 “275207042@qq.com” 分为三部分。
strerror、perror - 错误报告函数
1.strerror
char *strerror( int errnum );
strerror函数可以把错误码转换为对应的错误信息,返回错误信息对应字符串的起始地址。
#include <errno.h>//必须包含的头文件
我们需要知道,库函数在使用的时候如果发生错误,都会有对应的错误码,而这些错误码都会被存放在errno这个全局变量中,如果要使用这个全局变量,我们需要引其对应的头文件:#include<errno.h>
举例:
strerror: 只负责将错误码转换为对应的错误信息,不打印
和strerror有相似功能的还有perror函数
2.perror
void perror( const char *string );
相比于strerror函数,perror函数会首先把错误码转化为错误信息,然后打印错误信息(包含了自定义的信息),更方便实用。
举例:
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{//打开文件失败的时候,会返回空FILE* pf = fopen("test.txt", "r");//打开test.txt文件阅读if (pf == NULL){//两者比较printf("%s\n", strerror(errno));perror();return 0;
}
很显然,同样的效果,perror使用起来更简洁方便。
– the End –
好了,今天关于“str家族”的分享就到这里了。
期待下次再见~~~
【str家族】如何使用处理字符和字符串的库函数相关推荐
- C语言字符与字符串的库函数
本章重点介绍字符和字符串的库函数使用,C语言本身是没有字符串类型的,字符串通常存放在常量字符串或字符数组中.字符串常量适用于那些对它不做修改的字符串函数. 1.strlen:求字符串的长度(不包括 ...
- 给定一个字符串str,将str中连续两个字符为a的字符替换为b(一个或连续超过多个字符a则不替换)...
需求:给定一个字符串str,将str中连续两个字符为a的字符替换为b(一个或连续超过多个字符a则不替换) 如: a 不替换 b 不替换 ab 不替换 ba 不替换 aba 不替换 aab ...
- Python中两个list取交集、并集、差集以及为字符串str添加、插入特定字符的操作总结
Python中两个list取交集.并集.差集以及为字符串str添加.插入特定字符的操作总结 Python中两个list取交集.并集.差集 为字符串str添加.插入特定字符的操作总结 Python中两个 ...
- SQL分割字符串,SQL按照指定字符分割字符串,SQL处理字符串...
SQL分割字符串,SQL按照指定字符分割字符串,SQL处理字符串 -----原文来源于网络 T-SQL对字符串的处理能力比较弱,比如我要循环遍历象1,2,3,4,5这样的字符串,如果用数组的话,遍历 ...
- java 字符转化字符串_【转载】java字符串的各种编码转换
来自:http://www.blogjava.net/rabbit/archive/2008/03/27/189009.html import java.io.UnsupportedEncodingE ...
- 重温CLR(十) 字符、字符串和文本处理
本章将介绍.net中处理字符和字符串的机制 字符 在.NET Framewole中,字符总是表示成16位Unicode代码值,这简化了国际化应用程序的开发. 每个字符都表示成System.Char结构 ...
- java中的字符,字符串,数字之间的转换(亲测)
string 和int之间的转换 string转换成int :Integer.valueOf("12") int转换成string : String.valueOf(12) ch ...
- android 查找字符在字符串的位置
昨天,自己用到在字符串内查找一个字符串的位置,主要用到了 indexOf()的代码,这个是判断字符在字符串的第一次出现的位置.今天,自己没有什么好写的,所以决定把这个记录一下.也是很有用的. J ...
- python字符复制函数是啥_Python最全的字符和字符串函数,直接复制到IDLE或另存为py可以运行...
## -*- coding: utf-8 -*- import string str1 = "0123456789" print str1[0:3] ##截取第一位到第三位的字符 ...
最新文章
- 在Ubuntu 14.04 64bit上编译安装Crtmpserver trunk svn 811版本!
- 安全起见,这款 IDEA 插件赶紧删了吧!
- [翻译] VLDContextSheet
- 32接上拉5v_51单片机P0口上拉电阻的选择
- 【轻量级网络】MobileNet-v1详解
- python列表功能默写_python基础学习——列表list的功能
- 【李宏毅2020 ML/DL】补充:Meta Learning - Gradient Descent as LSTM
- JAVA中如何全局监听鼠标事件
- 中文停用词表(1893个)
- 使用Flutter开发一个仿微信飞机大战游戏
- 项目文档:IT项目管理
- 基于opencv 的OCR小票识别(1)
- python 正态分布函数_正态分布概率计算
- Lum Proxy全球代理IP,真实家庭住宅网络!
- 【电路设计】Altium Designer 20 PCB设计
- 餐饮日销售情况分析仪
- python 识别图像中的文字(数字)之python图文识别
- 看了这篇干货,再也不怕Mac内存不足了!
- OpenCV小案例(2)——判断一张图片中多少种颜色
- js函数使用详细讲解!