字符串函数strcpy、strcat、strcmp、strncpy、strncat、strncmp、strchr、strrchr、strpbrk、strstr、strspn、strcspn等

复制字符串:strcpy

原型:

char *strcpy( char *dst, char const *src );

作用:把参数src字符串复制到dst参数。

注意:参数src和参数dst在内存中如果出现重叠,其结果是未定义的,可能会有意想不到的错误。由于dst参数将进行修改,所以他必须是字符串数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。

目标参数以前的内容将被覆盖丢失。即使新的字符串比dst原先的内存更短,因为新字符串是以NUL字节结尾,所以老字符串最后剩余的几个字符串也会被有效的删除,这里说的删除不是物理上的删除,原有的老字符串还是存在,但是以后访问时,访问到新字符串的NUL字节就会结束访问,根本不会去访问到剩余的老字符串,所以他们是已经被丢失的了。

警告:目标字符串数组空间必须足够大,能够容纳需要复制的字符串,不然可能会把新的字符串剩余部分复制到数组后面的空间中,这将是一个隐形的灾难,可能会因此发生意想不到的问题,这是strcpy本身无法控制的,需要程序员自己去控制。

连接字符串:strcat

原型:

char *strcat( char *dst, char const *src);

作用:把参数src字符串拼接到dst字符串末尾。

注意:src和dst存储空间同样是不可以重叠的,返回结果是未定义的。

警告:依然要注意长度问题,要确保dst后面的数组剩余空间足够容纳src字符串。

函数的返回值:strcpy、strcat

strcpy和strcat都返回他们第一个参数的一份拷贝,实际上就是指向目标字符串数组的指针。由于他们返回这种类型的值,所以在使用时如果有需要可以嵌套的去使用,如下面的例子:

strcat( strcpy( dst, a ), b);

strcpy首先执行。把字符串从a复制到dst,并返回dst的一份拷贝。然后这个返回值成为strcat的第一个参数,strcat函数把b拼接至dst的后面。

但是这种嵌套的可读性貌似并不好,为了良好的可读性,建议还是使用下面的方式,毕竟你写的代码永远不是只为了给你一个人看,在效率乃至其他问题面前,代码的可读性也是一个至关重要的指标。

strcpy( dst, a );
strcat( dst, b );

注:这些函数绝大部分调用中,返回值都会被简单地忽略。

字符串比较:strcmp

比较两个字符串设计对两个字符串对应的字符逐个进行比较,直到发现不匹配为止。那个最先不匹配的字符串中较“小”(也就是说,在字符集中的序数较小即在ASCII码中的值)的那个字符所在的字符串被认为小于另一个字符串。如果其中一个字符串是另一个字符串的前面一部分,那么他也是“小”,因为他的NUL字节出现的更早。这种比较就是词典比较,对于只包含大写字母或者只包含小写字母的字符串比较,这种比较过程所出的结果总是和我们日常所用的字母顺序的比较相同。

原型:

int strcmp( char const *s1, char const *s2 );

如果s1小于s2,strcmp函数会返回一个小于0的值,如果s1大于s2,函数返回一个大于0的值。如果两个字符串相等,函数就返回0.

标准没有规定用于提示不相等的具体值。他只是说如果第一个字符串大于第二个字符串就返回一个大于零的值,如果第一个字符小于第二个字符就返回一个小于零的值。一个常见的错误就是返回1和-1,分别代表大于和小于。这是不对的。

长度受限的字符串函数:strncpy、strncat、strncmp

这俩函数接受显式的长度参数,用于限定进行复制或比较的字符数。这些函数提供了一种方便的机制,可以防止难以预料的长字符串从他们的目标数组溢出。

原型:

char *strncpy( char *dst, char const *src, size_t len );
char *strncat( char *dst, char const *src, size_t len );
int strncmp( char const *s1,char const *s2 size_t len );

作用:和strcpy一样,strncpy 把源字符串的字符复制到目标数组。然而,它总是正好向dst 写入len 个字符。如果strlen(sre)的值小于len,dst数组就用额外的NUL字节填充到len长度。如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中。注意!它的结果将不会以NUL字节结尾。

警告:1、使用这些函数时,依然记得dst和src的内存地址不可以重叠

警告:2、strnepy调用的结果可能不是一个字符串,因此字符串必须以NUL字节结尾。如果在一个需要字符串的地方( 例如strlen函数的参数)使用了一个不是以NUL字节结尾的字符序列,会发生什么情况呢? strlen 函数将无法知道NUL字节是没有的,所以它将继续进行查找,一个字符接一个字符, 直到它发现一个NUL字节为止。或许它找了几百个字符才找到,而strlen函数的这个返回值从本质上说是一个随机数。或者,如果函数试图访问系统分配给这个程序以外的内存范围,程序就会崩溃。

以上的问题在不受限函数中要格外注意,但是在使用时我们可以人为的在字符串数组最后一个元素改变复制给一个‘\0’,这是必要的

 strncat虽然也是一个长度受限的函数,但是它会自动在复制完len个字符后自动添加一个NUL字节,而且也不会像strncpy那样对目标数组的剩余空间用NUL填充。strncpy不会管目标数组剩多少空间,就按照地址往里复制最多len个字节(假设len为10,可能实际src里只有5个,所以用了最多这个词)。

strncmp用于比较两个字符串,比较两个字符串的前len个字符是否有不相等字符,如果有立即停止比较,并相应的返回,返回值与strcmp相同。

查找一个字符:strchr、strrchr

原型:

char *strchr( char const *str, int ch );
char *strrchr( char const *str, int ch );

注意:他们的第二个参数是一个整型值,但是包含了一个字符。

作用:strchr在字符串str中查找字符ch第一次出现的位置,找到后返回一个指向该位置的指针,如果查找失败就返回一个NULL指针。

作用:strrchr的功能与strchr基本一致,只是他所返回的是一个指向该字符最后一次出现的位置(最右边那个)。ps:个人观点:看下二者的字母,strrchr中间正好多了个r,Right的缩写?

查找任何几个字符:strpbrk

原型:

char *strpbrk( char const *str, char const *group );

作用:查找任一组字符group 第一次在字符串str中出现的位置,如果未找到匹配,函数返回一个NULL指针,实则就是看str中是否包含group中是否包含某几个字符,监测到任意一个就会返回。

查找一个子串:strstr

原型:

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

作用:在s1中查找是否包含字符串s2(此时s2是s1的一个子串),函数查找s2第一次出现的起始位置,并返回指向该位置的一个指针。如果s2并没有完整的出现s1的任一个地方,返回NULL指针,如果s2是一个空字符串,返回s1.

查找子串的最后出现位置:strrpbrk、strrstr

在标准库中并没有这两个函数,但是可以自己很轻易地实现。

#include <string.h>
char *my_ strrstr ( char const *s1, char const *s2 )
{
    register char *last;
    register char *current; 
    /*
    **由把指针初始化为我们已经找到的前一次匹配位置。
    */
    last = NULL;
    /*
    **只在第2个字符串不为空时才进行查找,如果s2为空,返目NOLL.
    */
    if( *s2 != NULL)
    {
        /*
        **.查找z2在s1中第1次出现的位置.
        */
        current = strstr( s1, s2 );
        /*
        **我们每次找到字符审时,让指针指向它的起始位置。然后查找该字符串下一个匹配位置。
        */
        while( current != NULL )
        {
            last = current;
            current = strstr( last + 1, s2 );
        }
    }

    /*返回指向我们找到的最后-次匹配的起始位置的指针。*/
    return last;
}

高级字符串查找:strspn、strcspn

原型:

size_t strspn( char const *str, char const *group );
size_t strcspn( char const *str, char const *group );

作用:strspn和strcspn函数用于在字符串的起始位置对字符计数。

group字符串指定一个或多个字符。strspn返回str起始部分匹配group中任意字符的字符数。例如,如果group包含了空格、制表符等空白字符,那么这个函数将返回str起始部分空白字符的数目。str的下一个字符就是他的第一个非空白字符。

即只要str从开头开始逐个检查每一个字符,是否在group里存在,知道检测到一个不存在时停止计数并返回数值。

下面的例子:

int len1, len2;
char buffer[] = "25,142,330,Smith,J,239-4123";

len1 = strspn( buffer, "0123456789" );
len2 = strspn( buffer, ",0123456789" );

根据定义,len1时,buffer中第一个字符2在“0123456789”中可以找到,5也可以,但是逗号“,”就找不到,所以计数到此,数值为2(找到两个)。

len2时,buffer中第一个字符2在“,0123456789”中可以找到,5也可以,逗号“,”也可以找到,算下啦正好11个。

下面的代码将计算一个指向字符中第一个非空白字符的指针(也可以设计成其他的字符):

ptr = buffer + strspn( buffer, "\n\r\f\t\v" );

strcspn函数正好和strspn函数相反,他对str字符串起始部分中不与group中任何字符匹配的字符进行计数。strcspn中的字母c源于对一数组求补这个概念,也就是把这些字符换成原先并不存在的字符。如果你使用“\n\r\f\t\v”作为group参数,这个函数将返回第一个参数字符串起始部分所有非空白字符的值。

总结一下就是strspn是从开头开始str逐个字符对比,在group中就计数,遇见不在其中的结束计数。而strcspn是与之相反,不在其中就计数,遇见在其中的就结束计数。

查找标记:strtok

原型:
 

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

作用:这个任务正是strtok函数所实现的功能。它从字符串中隔离各个单独的称为标记(token)的部分,并丢弃分隔符。

sep参数是个字符串,定义了用作分隔符的字符集合。第1参数指定-一个字符串,它包含零个或多个由sep字符串中一个或多个分隔符分隔的标记。strtok找到str的下一个标记,并将其用NUL结尾,然后返回一个指向这个标记的指针。
警告:当strtok函数执行任务时,它将会修改它所处理的字符串。如果源字符串不能被修改,那就复制一份,将这份拷贝传递给strtok函数。

其中str是要分解的字符串,sep是字符串中用来分解的字符,该函数返回分解后的字符串的起始位置指针。之所以是分解,就是说并没有生成新的字符串,只是在源字符串上面做了一些手脚,使得源字符串发生了变化,所以一定要注意,源字符串发生了变化!!!(如果是不可改变的const字符串,要自己先复制一份传入,此时传入的被改的就是拷贝的那份)。

看下例子:
 

// 例1
#include <string.h>
 
void main()
{  
    char s[] = "192.168.0.26";  
    char *delim = ".";  
    char *p;  
    printf("%s ", strtok(s, delim));  
 
    while((p = strtok(NULL, delim)))    
        printf("%s ", p);   
 
    printf("\n");
}

从例1中就可以看出strtok函数的基本使用方法,输入一个字符串数组,然后就可以将其按照一定的分隔符(例1中为".")将一个长的字符串分割成一个个短的字符串。这里需要注意的是,在对一个长字符串分割的时候,第一次调用时,strtok函数的第一个参数传入要分割的字符串,而第二次以及后面再次调用该函数的时候,strtok函数的第一个参数应该传入NULL,这是因为在strtok第一个参数为NULL的时候,该函数默认使用上一次未分割完的字符串的未分割的起始位置作为本次分割的起始位置,直到分割结束为止。

这里首先需要强调的是strtok函数在进行字符串分解的时候,其第一个参数,即char str[]是在变化的,就像前面说过的一样,其只是对源字符串动了一些手脚,也就是改变了源字符串。以例1中的字符串来说,"192.168.0.26"是源字符串,在调用strtok对其分解结束后,字符串变成了"19201680026",注意其中红色加粗部分是把'.'替换成了'\0',也就是替换成了字符串结束标志字符,这样在打印或使用的时候都会使得前面的字符串成为一个看起来独立的字符串,即"192"、"168"、"0"、"26",这些字符串还是在源字符串中,只是后面都有了自己的字符串结束标志'\0'而已。

 如果strtok函数要分解的字符串的首字符就是分隔符,那么strtok函数会忽略第一个字符,直接从下一个分隔符算起。比如:".192.168.0.26",那么第一个字符'.'就会被忽略,分解后的第一个字符串就是"192"。

错误信息:strerror

原型:

char *strerror( int error_number );

当你调用-些函数,请求操作系统执行一些功能如打开文件时,如果出现错误,操作系统是通过设置一一个外部的整型变量errmo进行错误代码报告的。strerror 函数把其中-一个错误代码作为参数
并返回一个指向用于描述错误的字符串的指针。
C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。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);
}

让我们编译并运行上面的程序,这将产生以下结果,因为我们尝试打开一个不存在的文件:

Error: No such file or directory

字符操作:

标准库包含了两组函数,用于操作单独的字符,它们的原型位于头文件ctype.h。 第1组函数用于对字符分类,而第2组函数用于转换字符。


字符分类:

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行‘\n’,回车‘\r’,制表符‘\t’或垂直制表符‘\v’
isdegit十进制数字0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或a~z
isalnum字母或数字,a~z,A~Z,或0~9
ispunct标点符号,任何不属于数字或字母的图形字符可打印符号()
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

字符串换:tolower、toupper

原型:

int tolower( int ch );
int toupper( int ch );

toupper函数返回其参数的对应大写形式,tolower 函数返回其参数的对应小写形式。如果函数的参数并不是一个处于适当大小写状态的字符(即toupper的参数不是小写字母或tolower的参数不是个大写字母),函数将不修改参数直接返回。
提示:

直接测试或操纵字符将会降低程序的可移植性。例如,考虑下面这条语句,它试图测试ch是否是一个大写字符。
 

if( ch >= 'A' && ch <= 'Z')

这条语句在使用ASCII字符集的机器上能够运行,但在使用EBCDIC字符集的机器上将会失败。另一方面,下面这条语句
 

if( isupper( ch ) )

无论机器使用哪个字符集,他都能顺利运行。

内存操作:memcpy、memmove、memcmp、memchr、memset

根据定义,字符串由一一个NUL字节结尾,所以字符串内部不能包含任何NUL字符。但是,非字符串数据内部包含零值的情况并不罕见。你无法使用字符串函数来处理这种类型的数据,因为当它们遇到第1个NUL字节时将停止工作。不过,我们可以使用另外- -组相关的函数,它们的操作与字符串函数类似,但这些函数能够处理任意的字节序列。下 面是它们的原型。
 

void *memcpy( void *dst, void const *src, size_t length );
void *memmove( void *dst, void const *src, size_t length );
void *memcmp( void const *a, void const *b, size_t length );
void *memchr( void const *a, int ch, size_t length );
void *memset( void *a, int ch, size_t length );

每个原型都包含一个显式的参数说明需要处理的字节数。但和strn带头的函数不同,它们在遇到NUL字节时并不会停止操作。memcpy从src的起始位置复制length个字节到dst的内存起始位置。你可以用这种方法复制任何类型的值,第3个参数指定复制值的长度(以字节计)。如果src和dst以任何形式出现了重叠,它的结果是未定义的。

例如:

char temp[SIZE], values[SIZE];
memcpy( temp, values, SIZE)

他从数组value复制SIZE个字节到数组temp。

但是如果这两个数组都是整形数组该怎么办,下面的语句可以完成:

memcpy( temp, values, sizeof( values ) );

其实这样来想一想,传入的两个数组其本质都是指针,不要被表象所干扰,他俩都是分别指向各自内存地址的指针,然后只要给出需要读取的长度就可以,sizeif求得值,就是这个数组的所占总字节数。

前面两个参数不需要使用强制类型转换,因为在函数的原型中,参数的类型是void*型指针,而任何类型的指针都可以转换为void*型指针。

如果数组只有部分内容需要被复制,那么需要复制的数量必须在第3个参数中指明。对于长度大于一个字节的数据,要确保把数量和数据类型的长度相乘,例如:
 

memcpy( saved_answers, answers, count * sizeof(anwers[0] ) );

这个精髓就是求出这个数组单个元素的所占字节,然后根据想要求得元素数量,乘单位字节,得到的就是目标数量元素所占的总子节数。

memmove函数的行为和memcpy差不多,只是它的源和目标操作数可以重叠。虽然它并不需要以下面这种方式实现,不过memmove的结果和这种方法的结果相同:把源操作数复制到-个临时
位置,这个临时位置不会与源或目标操作数重叠,然后再把它从这个临时位置复制到目标操作数。memmove通常无法使用某些机器所提供的特殊的字节字符串处理指令来实现,所以它可能比
memcpy慢一些。但是,如果源和目标参数真的可能存在重叠,就应该使用memmove,如下例所示:
 

/*
**Shift the value in the x array left one position
*/
memmove( x, x + 1, ( count - 1) * sizeof( x[0] ) );

memcmp对两段内存的内容进行比较,这两段内存分别起始于a和b,共比较length个字节。这些值按照无符号字符逐字节进行比较,函数的返回类型和strcmp函数一样一负 值表示a小于b, 正值表示a大于b,零表示a等于b。由于这些值是根据-串无符号字节进行比较的,所以如果memcmp函数用于比较不是单字节的数据如整数或浮点数时就可能给出不可预料的结果。
memchr从a的起始位置开始查找字符ch第1次出现的位置,并返回一个指向该位置的指针,它共查找length个字节。如果在这length个字节中未找到该字符,函数就返回一个NULL指针。最后,memset函数把从a开始的length个字节都设置为字符值ch。例如:
 

memset( buffer, 0, SIZE );

把buffer的前SIZE个字节都初始化为0。.
 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值