本期主要介绍一些常见字符串相关函数及模拟实现
目录
1.1 求字符串长度strlen
size_t strlen ( const char * str )
功能: 求字符串长度
注意事项:
字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )
1.2 字符串复制strcpy
char* strcpy(char * destination, const char * source )
功能: 把含有“'\0'”结束符的字符串复制到另一个地址空间。strcpy是一种C语言的标准库函数,返回值的类型为“char*”;strcpy是“string copy”(字符串复制)的缩写
头文件:#include<string.h> 和 #include<stdio.h>
把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
1.3 字符串连接 stract
char * strcat ( char * destination, const char * source )
功能: 将两个char类型连接
在C中,函数原型存在 <string.h>头文件中。在C++中,则存在于<cstring>头文件中。
把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。
要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
1.4比较两个字符串大小strcmp
int strcmp ( const char * str1, const char * str2 )
功能:比较两个字符串大小
当s1<s2时,返回为负数;
当s1=s2时,返回值= 0;
当s1>s2时,返回正数。
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:
1."A"<"B" 2."A"<"AB" 3."Apple"<"Banana" 4."A"<"a" 5."compare"<"computer"
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。
ANSI标准规定,返回值为正数,负数,0 。而确切数值是依赖不同的C实现的。 [3]
当两个字符串不相等时,C标准没有规定返回值会是1 或 -1,只规定了正数和负数。
有些会把两个字符的ASCII码之差作为比较结果由函数值返回。但无论如何不能以此条依据作为程序中的流程逻辑。
1.5 指定长度字符串复制strncpy
strncpy函数用于将指定长度的字符串复制到字符数组中,是 C语言的库函数之一,来自 C语言标准库,定义于 string.h。
语法形式为:char *strncpy(char *dest, const char *src, int n),表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest。
注意事项:
1)source串长度<=dest串长度(这里的串长度包含串尾NULL字符)
如果n<source由于长度达到source NULL,正常复制,特别注意,如果source中有NULL,strncpy复制到NULL即使没到n也提前停止。如果n = source串长度,与strcpy一致。注意n的选择当n > destin串长度,destin栈空间溢出产生崩溃异常。
2)source串长度>destin串长度
如果n =destin串长度,则destin串没有NULL字符,会导致输出会有乱码。如果不考虑source串复制完整性,可以将dest 最后一字符置为'\0'。
综上,一般情况下,使用strncpy时,建议将n置为dest串长度(除非你将多个source串都复制到destin数组,并且从dest尾部反向操作),复制完毕后,为保险起见,将dest串最后一字符置NULL,避免发生在第2)种情况下的输出乱码问题。当然喽,无论是strcpy还是strncpy,保证dest串容量(能容纳下source串)才是最重要的。
1.6 字符串结尾追加strncat
char *strncat(char *dest, const char *src, size_t n)
功能:把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
#include <string.h>
dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
src -- 要追加的字符串。
n -- 要追加的最大字符数。
注意事项:
src和dest所指内存区域不可以重叠,并且dest必须有足够的空间来容纳src的字符串。
strncat()将会从字符串src的开头拷贝n 个字符到dest字符串尾部,dest要有足够的空间来容纳要拷贝的字符串。如果n大于字符串src的长度,那么仅将src指向的字符串内容追加到dest的尾部。
strncat()会将dest字符串最后的'\0'覆盖掉,字符追加完成后,再追加'\0'。
1.7 字符串比较函数strncmp
strncmp函数为字符串比较函数,字符串大小的比较是以ASCII 码表上的顺序来决定,此顺序亦为字符的值。
函数声明为int strncmp ( const char * str1, const char * str2, size_t n );
功能是把 str1 和 str2 进行比较,最多比较前 n 个字节.
若str1与str2的前n个字符相同,则返回0;
若s1大于s2,则返回大于0的值;
若s1 小于s2,则返回小于0的值。
1.8 字符串查找strstr
strstr(str1,str2)
包含文件<string.h>
用于判断字符串str2是否是str1的子串。
如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;
否则,返回NULL。
1.9 分割字符串strtok
char *strtok(char s[], const char *delim);
分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。头文件<string.h>
strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
注意:strtok函数会破坏被分解字符串的完整,调用前和调用后的s已经不一样了.所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。
1.10 返回错误码strerror
通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误.
#include <errno.h> #include <string.h>
返回错误码,所对应的错误信息
下面的实例演示了 strerror() 函数的用法。
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE *fp;
fp = fopen("file.txt","r");
if( fp == NULL )
{
printf("Error: %s\n", strerror(errno));
}
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果,因为我们尝试打开一个不存在的文件:
1.11内存拷贝memcpy
memcpy指的是C和C++使用的内存拷贝函数
函数原型为void *memcpy(void *destin, void *source, unsigned n);
C语言:#include<string.h> C++:#include<cstring>
函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。
destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
source-- 指向要复制的数据源,类型强制转换为 void* 指针。
n-- 要被复制的字节数
该函数返回一个指向目标存储区destin的指针。
注意事项
1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。
2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
注意:source和destin都不一定是数组,任意的可读写的空间均可。
1.12 内存区域复制memmove
原型:void *memmove( void* dest, const void* src, size_t count );
头文件:<string.h>
功能:由src所指内存区域复制count个字节到dest所指内存区域
memmove用于拷贝字节,
如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
注意
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理
1.13按字节比较memcmp
函数的原型为 int memcmp(const void *str1, const void *str2, size_t n));
其功能是把存储区 str1 和存储区 str2 的前 n 个字节进行比较。该函数是按字节比较的,位于string.h。
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2
2.库函数的模拟实现
2.1 strlen模拟实现
//计数器方式
int my_strlen(const char * str)
{
int count = 0;
while(*str)
{
count++;
str++;
}
return count;
}
//不能创建临时变量计数器
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
//指针-指针的方式
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
2.2 strcpy模拟实现
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
2.3 strcat模拟实现
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}
2.4 strstr模拟实现
char * strstr (const char * str1, const char * str2)
{
char *cp = (char *) str1;
char *s1, *s2;
if ( !*str2 )
return((char *)str1);
while (*cp)
{
s1 = cp;
s2 = (char *) str2;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
}
还有一种较为简便的KMP算法,大家可以自行查找。
2.5 strcmp模拟实现
int my_strcmp (const char * src, const char * dst)
{
int ret = 0 ;
assert(src != NULL);
assert(dest != NULL);
while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
++src, ++dst;
if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;
return( ret );
}
2.6 memcpy模拟实现
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst);
assert(src);
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
2.7 memmove模拟实现
void * memmove ( void * dst, const void * src, size_t count)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}