c语言:字符串

字符串长度:

字符串以NUL('\0')结尾,包含在 string.h 头文件中。

产度就是包含字符的个数。 标准库保留了所有以 str 开头的函数

库函数  strlen   原型如下:


size_t strlen(  char const *string ) ;

注意它的返回值是 size_t 类型,在头文件 stddef.h  中定义,是 usigned int 类型,若是在表达式中使用无符号数,可能出现不可预知的结果。eg:

if( strlen( x ) > = strlen( y ) )  ...  和   if( strlen( x) - strlen( y ) >= 0  )...

事实上它们是不相等的。第二个语句结果永远是真,strlen的结果是无符号数,所以 >= 左边的表达式是无符号数,而无符号数不会是负数。


表达式中如果同时包含signed和unsigned,可能会产生奇怪的结果。比如下面的语句:

if ( strlen( x ) >= 10 )...      if ( strlen( x ) - 10 >= 0 )

原因和上面一样,如果你把strlen的返回值转换为int就能够消除这个结果。


提示: 你可能想自己编写库函数,灵活运用 register声明和一些技巧,但,库函数有时候是用汇编语言实现的,目的就是充分发挥机器的特殊字符串操作指令,即使没有,我们应该把时间花在程序其他部分的算法改进上。  寻找一种更好的算法比改进一种差劲的算法要更有效率,复用已经存在的软件要比重新开发一个效率更高。


不受限制的字符串函数:

用于复制的函数是 strcpy ,原型如下:

char  *strcpy( char *dst, char const *src );
如果参数src 和 dst 重叠,结果是未定义的。由于dst参数会被修改,它应当是一个字符数组或者一个指向动态分配内存的数组的指针,不能用字符串常量。 目标参数以前的内容会被覆盖掉并丢失,新串会以 NUL结尾。 复制之前,程序员必须保证数组空间足够容纳,否则多余的字符会覆盖原先存储于数组后面的内存空间的值,而strcpy无法判断目标字符数组的长度,这样可以避免大量的debug。


连接字符串 strcat,原型如下:

char *strcat ( char *dst, char const *src );
dst可以为空,src的拷贝会添加到dst的末尾。如果dst和stc有重叠,结果是未定义的。 同样,程序员应该保证目标字符串的空间足够容纳最后的结果。


字符串的比较 strcmp ,原型如下:

int  strcmp ( char const *s1, char const *s2 );
比较字符串涉及字符的逐个比较,直到发现不匹配为止,结果只有大于,小于,等于之分,等于则返回0。
初学者往往会写成: if ( ! strcmp( a, b ) ) ; 
把这个返回值作为bool进行测试是一种坏的风格,更好的方法是将返回值与0做比较。


长度受限制的字符串函数


原型如下,如果dst和src发生重叠,strncpy和strncat结果是未定义的。

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

strncpy将src复制到dst,然而它总是正好向dst写入n个字符。 如果strlen(src)小于len,dst数组就用额外的NUL字符填充到len长度。 如果strlen(src)大于等于len,那么只有len个字符被复制到dst中,注意,这个结果将不会以NUL字符结尾!
这个问题只有当,使用strncpy创建字符串,然后对它们使用str开头的库函数,或者printf中使用%s格式码打印的时候会出问题。 在使用之前,必须确保字符串以NUL结尾,eg:

char  buffer[ SIZE ];

...

strncpy( buffer, name, SIZE );

buffer[ SIZE - 1 ] = '\0' ;

这样就能够确保它正确工作, strncpy最多向dst复制n个字符(再加一个NUL字符),它不管dst的空间够不够。


字符串查找基础:


查找一个字符:

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

第一个查找ch在str第一次出现的位置,第二个查找最后一次出现的位置,没找到都返回NULL指针。


查找任何几个字符:


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

它不是查找特定的字符,而是查找任何一组字符第一次在str中出现的位置。函数将指向第一个匹配group中任何一个字符的位置,未找到返回NULL。


查找一个子串:


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

函数在s1中查找整个s2第一次出现的位置,没找到返回NULL。如果s2是空指针,则返回s1.


我们可以利用strstr来实现strrstr或者strrpbrk,eg:


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

char *my_strrstr( char const *s1, char const *s2 )
{
    register char *last;
    register char *current;

    last = NULL;

    if( *s2 != '\0' ){
        current = strstr( s1, s2 );

        while( current != '\0' ){
            last = current;
            current = strstr( last+1, s2 );
        }
    }
    return last;
}


高级字符串查找:


查找一个字符串前缀:

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

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

group指定一个或者多个字符,strspn返回str起始部分匹配group中任意字符的字符数。
例如:
int  len1, len2;
char  buffer[] = "25,142,330,Smith,J,239-4213" ;
len1 = strspn( buffer, "0123456789" );
len2 = strspn( buffer, ",0123456789" );

这样,变量len1=2,len2=11.


下面的代码将计算一个指向字符串中第一个非空白字符的指针:

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

strcspn和strspn刚好相反。



查找标记:


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

strtok从字符串中隔离各个单独的称为标记的部分,丢弃分隔符。sep参数是字符串,定义了分隔符的字符集合。strtok找到str的下一个标记,并将其用NUL结尾,然后返回一个指向这个标记的指针。

警告 strtok会修改它所处理的字符串,如果源串不能修改,就复制一份拷贝传递给strtok。

流程: 如果strtok的第一个参数不是NULL,函数将找到字符串的第一个标记,strtok同时将保存它在字符串中的位置。如果strtok第一个参数是NULL,函数就在同一个字符串中从这个被保存的位置开始像前面一样查找下一个标记。 如果字符串内不存在更多的标记,strtok函数返回一个NULL指针。 典型的情况下,第一次调用strtok时,向它传递一个指向字符串的指针。 然后,这个函数重复调用(第一个参数为空),知道它返回NULL为止。

例子:

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

void print_tokens( char *line )
{
    static char whitespace[] = " \t\f\r\v\n";
    char *token;
        
    for( token = strtok( line, whitespace ); 
        token != NULL;
        token = strtok( NULL, whitespace ) )
        printf("the next token is :%s\n", token );
}

提取标记的时候,你可以在每次调用strtok函数时使用不同的分隔符集合,当一个字符串的不同部分由不同的字符集合分隔时,这个技巧很好!

警告: 由于strtok函数保存它所处理的函数的局部状态信息,所以你不能用它同时解析两个字符串!!!



字符操作:


字符分类函数:

如果符合条件,下列函数就会返回真:’
isspace : 空格,换页'\f',换行'\n',回车'\r',制表符'\t',垂直制表符'\v'
isdigit: 0~9十进制
isxdigit:十六进制,包括十进制数字,小写a~z,大写A~Z
islower:小写字母a~z
isupper:大写字母A~Z
isalpha:是否为字母(大写+小写)
isalnum:字母或者数字


字符转换:

int  tolower( int ch ) ;
int  tiupper( int ch ) ;

提示 直接测试或者操作字符将会降低可移植性,eg

if ( ch >= 'a' && ch <= 'z' )
这条语句在使用ascii字符集的及其能够运行,但是使用ebcdic字符集的机器上将会失效。
而这条:
if ( isupper( ch )) 无论什么字符集都可以运行。


最后我们来总结一下字符串吧:

一:应该在(signed)有符号数的表达式中使用strlen函数。(可以转换为int型)

二:不要在表达式中混用有符号数和无符号数。

三:使用strcpy函数把一个长字符串复制到一个较短的数组中。

四:使用strcat函数把一个字符串添加到一个数组中,到时数组溢出。

五:不要把strcmp函数的返回值当做bool值测试

六:不要把strcmp的返回值和1 以及 -1 比较

七:使用并非以NUL字符结尾的字符序列

:使用strncpy函数产生不以NUL字节结尾的字符串

九:把strncpy函数和strxxx族函数混用

:忘了strtok函数会修改它所处理的字符串

十一:strtok函数不能同时处理两个字符串


编程提示的总结:

一:不要试图自己编写功能相同的函数来取代库函数(可以模仿写,看源码,做尝试)。

二:使用字符分类和转换函数可以提高函数的移植性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值