C语言字符串处理函数(转)

转自:https://www.cnblogs.com/xionghj/p/4443891.html

1 字符串基础

字符串是一种重要的数据类型,有零个或多个字符组成的有限串行。

定义子串: 串中任意个连续的字符组成的子序列,并规定空串是任意串的子串,任意串也是其自身的子串,如字符串"adereegfb"中它本身、空串、诸如"ader"连续的字符串都是它的子串。子序列则不要求字符连续,但顺序要与主串保持一致,若有"abcd"与"ad"则两者的最长公共子序列为"ad"。在动态规划中计算最长公共子序列和最长公共子串中一定要能区分这两个概念!

在C语言中并没有显示的字符串类型,它有如下两种风格的字符串:

  • 字符串常量: 以双引号扩起来的字符序列,规定所有的字符串常量都由编译器自动在末尾添加一个空字符
  • 字符数组: 末尾添加了‘\0‘的字符数组,一般需要显示在末尾添加空字符。
char c1[]={‘c‘,‘+‘,‘+‘}; //末尾没有空字符
char c2[]={‘c‘,‘+‘,‘+‘,‘\0‘}; //末尾显示添加空字符
char c3="c++"; //末尾自动添加空字符

注意到通过字符数组初始化和字符串常量初始化并不完全相同的。因为字符串常量包含一个额外的空字符用于结束字符串,用它来初始化创建数组时,末尾会自动添加空字符。所以c1的长度是3,后两者的长度是4,并且字符数组c2和c3都被称为C风格字符串,而字符数组c1不是C风格字符串。

规定C风格的字符串都是以NULL空字符(‘\0‘)作为终结符结尾。由于它是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NULL字节,如strlen函数。而且C标准库中提供的各种字符串处理函数都要求提供的字符串或字符数组必须以空字符结束,否则会出现不可预料的结果。如:

char c[]={‘c‘,‘+‘,‘+‘};
printf("%d\n",strlen(c)); //结果输出为6,这是不正确的

2 标准库中的字符串处理函数

C标准库中头文件<string.h>定义了两组字符串函数(C++中用<string>表示)。

  • 第一组函数的名字以str开头,它主要处理以‘\0‘结尾的字符串,所以字符串内部不能包含任何‘\0‘字符。
  • 第二组函数的名字以mem开头,主要考虑非字符串内部含有零值的情形,它能够处理任意的字节序列,操作与字符串函数类似
  • 除了memmove函数外,其他函数都没定义重叠对象间的行为

为了提高程序在不同机器上的移植性,利用typedef定义新类型名,即typedef unsigned int size_t。 程序员必须要保证目标字符数组的空间能够足以存放结果字符串(有可能存在字符数组溢出的危险)

  • 字符串处理类

如下表为字符串处理函数说明,变量s,t的类型是char *, cs和ct的类型是const char *;n的类型为size_t,c的类型为int

C语言字符串处理函数(转)

  • 内存操作类

按照字节数组的方式操作对象,提供一个高效的函数接口(提供字节流的访问)。其中s,t类型是void * , cs,ct的类型是const void *; n类型为size_t,c类型为int

C语言字符串处理函数(转)

总结起来,头文件< string.h>实现了如下函数:

  • 长度计算、长度不受限和受限的复制、连接和比较版本的函数
  • 基础字符串查找(查找一个字符、一组字符和匹配一个子串)、高级字符串查找(查找子串前缀位置、返回token标记)
  • 处理任意字节序列的内存操作如复制、比较、查找和初始化等函数

2.1 手写字符串处理函数

A strlen/strcmp/strcpy/strcat等函数

代码实现和测试如下:

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

/***********************************
 *
 * 基本的字符串函数
 * strlen/strcmp/strcpy/strcat/strncmp/strncpy/strncat
 * strdup/strrev
 * atoi/strtod
 *
 **********************************/

size_t my_strlen(const char *src) {
    assert(src != NULL);
    /*方法1*/
    int len = 0;
    while(*src++ != ‘\0‘) {//函数退出条件是src=‘\0‘但之后还进行了自增运算
        len++;
    }
    return len;
    /*方法2*/
    // const char *psrc = src;
    // while(*psrc++ != ‘\0‘) ;
    // return psrc - src - 1;
}

/**
 * 切记不可用*s++ == *t++
 * 因为不相等时时还会继续比较一次,自增运算应放在循环体内
 */
int my_strcmp(const char *s,const char *t) {
    assert(s != NULL && t != NULL);
    while(*s == *t) {
    	if(*s == ‘\0‘)
    		return 0;
    	s++;
    	t++;
    }
    // return ((*(unsigned char *)s > *(unsigned char *)t) > 0)? 1: -1;
    return ((*s - *t) > 0)?1:-1;
}

int my_strncmp(const char *s,const char *t,size_t n) {
	assert(s != NULL && t != NULL);
	while(n-- && *s == *t) { //条件用n判断但之后n减少了1
		if(n == 0 && *s == *t)
			return 0;
		s++;
		t++;
		
	}
	return ((*s - *t) > 0)? 1: -1;
}


/**
 * 要求src和dst不重叠,且dst有足够空间
 */ 
char *my_strcpy(char *dst,const char *src) {
    if(src == dst) return dst;
    assert(src != NULL && dst != NULL);
    char *pdst = dst;
    while(*pdst++ = *src++);
    //*pdst = ‘\0‘; //该代码可以忽略
    return dst;
}

char *my_strncpy(char *dst,const char *src,size_t n) {
	assert(src != NULL && dst != NULL);
	char *pdst = dst;
	while(n-- > 0 && *src != ‘\0‘)
		*pdst++ = *src++;
	*pdst = ‘\0‘; //切记勿忘
	return dst;
}


char *my_strcat(char *dst,const char *src) {
	assert(src != NULL && dst != NULL);
	char *pdst = dst;
	while(*pdst) pdst++;
	while((*pdst++ = *src++) != ‘\0‘);
	//*pdst = ‘\0‘; //该行可以忽略
	return dst;
}


char *my_strncat(char *dst,const char *src,size_t n) {
	assert(src != NULL && dst != NULL);
	char *pdst = dst;
	while(*pdst) pdst++ ; 
	while(n-- && (*pdst++ = *src++)) ;
	*pdst = ‘\0‘;
	return dst;
}

/*字符串拷贝到新位置,需要配合free使用*/
char *my_strdup(const char *src) {
	if(src == NULL) return NULL;
	/*先计算字符串长度*/
	size_t len = my_strlen(src);
	char *new_addr = malloc(len + 1);
	char *res = new_addr;
	while((*new_addr++ = *src++) != ‘\0‘);
	return res;
	// 测试
	// char *str = my_strdup("hello world!");
	// printf("%s\n",str);
	// free(str);
}

char *my_strrev(char *src) {
	assert(src != NULL);
	char *s = src;
	char *t = src + my_strlen(src) - 1;
	while(s < t) {
		*s ^= *t;
		*t ^= *s;
		*s ^= *t;
		s++;t--;
	}
	return src;
	// 测试
	// char s[] = "hello";
	// printf("%s\n",my_strrev(s)); //不能使用字符串,因为字符串是常量,无法修改
}

/**
 * 字符串转整数即atoi
 * 如果第一个非空格字符存在,从数字或者正负号开始做类型转换,
 * 检测到非数字或者结束符时停止转换返回相应整数;否则溢出时就返回0
 * 判断字符串是不是数字,类似于strtod
 */
int my_atoi(const char *src) {
	const char *p = src;
	while(*p && (*p == ‘ ‘ || *p == ‘\t‘ || *p == ‘\n‘) ) p++;
	long long res = 0;
	bool flag = false;
	bool valid = false;
	if(*p == ‘+‘)
		p++;
	else if(*p == ‘-‘) {
		p++;
		flag = true;
	}
	/*检测到非数字字符时停止转换,返回整形数否则返回0*/
	for(;*p && (*p >= ‘0‘ && *p <= ‘9‘);p++) {
		int sign = (flag == true)?-1:1;
		res = res * 10 + sign * (*p - ‘0‘);
		if((flag && res < 0x80000000) || (!flag && res > 0x7fffffff)) {
			res = 0;
			break;
		}
	}
	if(*p == ‘\0‘) {
		valid = true;
	}
	return (int)res;
}

void test() {
	if(my_strlen("") == strlen(""))
		printf("test successful!\n");
	else
		printf("test failed\n");
	if(my_strlen("hello") == strlen("hello"))
		printf("test successful!\n");
	else
		printf("test failed\n");
  	if(my_strlen("hello world") == strlen("hello wrold"))
		printf("test successful!\n");

相关推荐