使用c实现String类型的部分功能

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>  // 断言库

#pragma warning(disable: 4996)
#define pause system("pause")

// 定义String类型
typedef struct STRING{
	char* data;   // 数据
	int length;   // 实际存储长度
	int capacity; // 容量 = 长度 + 1   字符串有'\0'
} Str;

// malloc  calloc  realloc

/*
当传递给函数的 data 指针 NULL 时,它会分配一个长度为2的字符串 "" 
代替长度为1的字符 '\0'。
这是因为需要一个表示空字符串的字节,
还需要存储表示空字符的 '\0' 字节
*/
// 初始化Str对象数据   initStrByData  // 栈区创建Str结构体变量,data数据在堆区
void initStrByData(Str* str, const char* data) {
	if (NULL == str)
	{
		printf("str is NULL.\n"); // 显示返回
		return;
	}
	if (NULL != str->data && str->capacity >= str->length && str->capacity >= 1 && str->length >= 0) // 如果已经存在数据,就清空释放掉
	{
		free(str->data);
		str->capacity = str->length = 0;
	}

	if (NULL != data)
	{
		str->length = strlen(data);
		str->capacity = str->length + 1;
		str->data = (char*)malloc((str->capacity) * sizeof(char));
		memset(str->data, 0, str->capacity);
		strncpy(str->data, data, str->length);
		str->data[str->length] = '\0'; // 强制设置终止字符,以确保字符串有效 ,其实前面已经清0了
	}
	else
	{
		str->length = 0; //无数据,就当成一个空串""
		str->capacity = 2;
		str->data = (char*)malloc(2 * sizeof(char));
		str->data[0] = '\0';
	}
}

// 初始化Str对象容量   initStrByCapacity
void initStrByCapacity(Str* str, int capacity) {
	if (NULL == str)
	{
		printf("str is NULL.");
		return;
	}  // 有点问题 二次分配data 空间,对于已经有内容的如何处理?
	if (NULL != str->data && str->capacity >= str->length && str->capacity >= 1 && str->length >= 0) // 如果已经存在数据,就清空释放掉
	{
		free(str->data);
		str->capacity = str->length = 0;
	}
	if (capacity <= 0)
	{
		str->capacity = 1;
		str->length = 0; // 实际存储长度
		str->data = (char*)calloc(str->capacity, sizeof(char));
	}
	else
	{
		str->capacity = capacity;
		str->length = 0;
		str->data = (char*)calloc(str->capacity, sizeof(char));
	}
}

// 初始化Str对象容量  //创建于栈区的String     createStrByCapacity
Str* createStrByCapacity(Str** str, int capacity) {
	*str = (Str*)malloc(1 * sizeof(Str)); //申请空间
	initStrByCapacity(*str, capacity);
	return *str;
}

// 初始化Str对象数据  //创建于栈区的String    createStrByData
Str* createStrByData(Str** str, const char* data) {
	*str = (Str*)malloc(1 * sizeof(Str));
	initStrByData(*str, data);
	return *str;
}


// 释放Str对象的data占用的内存
void freeStr(Str* str) {
	if (NULL != str)
	{
		if (NULL != str->data)
		{
			free(str->data);
			str->data = NULL;
		}
		str->capacity = str->length = 0;
	}
}

// 释放Str对象占用的内存
void deleteStr(Str** str) {
	if (NULL != str)
	{
		if (NULL != *str)
		{
			if (NULL != (*str)->data)
			{
				free((*str)->data);
				(*str)->data = NULL;
			}
			(*str)->capacity = (*str)->length = 0;
			*str = NULL;
		}
	}
}


// 获取Str对象的长度
int getStrLength(const Str* str) {
	if (NULL != str){
		return str->length;  
	}
	return -1;
}

// 获取Str对象的容量
int getStrCapacity(const Str* str) {
	if (NULL != str){
		return str->capacity;
	}
	return -1;
}



//注意,返回值应当判断,否则,返回值为-1时,以字符形式输出,其显示可能时未知的。
// 获取Str对象指定位置的字符
char getCharAtIndex(const Str* str, int index) {
	if (NULL != str)
	{
		if (index >= 0 && index < str->length) {
			return str->data[index];
		}
	}
	return -1; // 字符不存在
}

// 输出Str对象的内容  
void printStr(const Str* str) {
	if (NULL != str){
		if (NULL != str->data)
		{
			//printf("%s\n", str->data);
			puts(str->data);
		}
	}
	else
	{
		printf("%s\n","\"NULL\"");
	}
}

// 拼接两个Str对象
Str* concatStr(Str* str1, const Str* str2) {
	if(NULL != str1)
	{
		if (NULL != str2)
		{
			int len1 = str1->length;
			int len2 = str2->length;
			if ((len1 - len2) < (len2 + 1))
			{
				char* tmp = (char*)realloc(str1->data, len1 + len2 + 1);
				if (NULL == tmp) {
					return NULL; // 内存分配失败
				}
				// 重新分配存储空间 
				str1->data = tmp;
			}
			strcat(str1->data, str2->data);
			str1->length = len1 + len2;  // 更新实际长度
			str1->capacity = str1->length + 1;  // 更新容量
			str1->data[len1 + len2] = '\0'; // 防止打印越界
			
		}
		return str1;

	}
	return NULL;

}

// 比较两个Str对象是否相等
int compareStr(const Str* str1, const Str* str2) {
	if (NULL != str1 && NULL != str2)
	{
		if (NULL != str1->data && NULL != str2->data)
		{
			// 为了避免strcmp的返回值造成混淆,定义其返回结果
			int cmp = strcmp(str1->data, str2->data);
			if (cmp == 0) return 0; // str1 == str2
			else if (cmp < 0) return -1; // str1 < str2
			else return 1; // str1 > str2
		}
	}
	return -2;  // 无法比较
	
}

// 截取Str对象的子字符串  
Str* subStr(const Str* str, int startIndex, int length) {   // && 具有短路效果,条件必须全部满足
	if (NULL != str && str->data != NULL && startIndex >= 0 && length >= 0)  
	{
		// 因为 当str->data,没有正确的初始化,strlen函数的结果可能时未知的
		// 好在初始化的时候就已经设置为'\0'结尾,还需判断str->data != NULL就行了
		int strLength = strlen(str->data);  // 和str->length一个效果
		if (startIndex <= strLength && length <= strLength - startIndex)
		{			
			// 可截取长度  length <= strLength - startIndex		
			Str* result = (Str*)malloc(1 * sizeof(Str));
			initStrByCapacity(result, length + 1);  // 为新字符串分配空间
			strncpy(result->data, (str->data) + startIndex, length);  // 复制数据
			result->length = strlen(result->data); // 更新实际长度
			return result;
		}
	}
	return NULL;
}


// 查找字符串中第一次出现指定子字符串的位置
int findSubStr(const Str* str, const char* subStr) {
	if (NULL != str && NULL != subStr)
	{
		// subStr的长度是否大于str,不做校验,因为使用strlen可能会出问题
		char* found = strstr(str->data, subStr);
		if (NULL != found) {
			return found - str->data;
		}
	}
	return -1; // 不存在返回-1
}

// 替换字符串中的指定子字符串
Str* replaceSubStr(Str* str, const char* oldSubStr, const char* newSubStr) {
	if (NULL != str && NULL != oldSubStr && NULL != newSubStr)
	{
		int oldSubStrLen = strlen(oldSubStr);
		int newSubStrLen = strlen(newSubStr);

		// 先搜索old字串是否存在
		char* found = strstr(str->data, oldSubStr);
		if (NULL != found) {
			// todo
			// 首先计算新字符串的长度
			int newStrLen = str->length - oldSubStrLen + newSubStrLen;
			// 然后为新字符串分配内存空间
			char* newStrData = (char*)malloc(newStrLen + 1);
			if (NULL == newStrData) {
				// 内存空间不足
				return NULL;
			}
			// 将基础字符串str前found的部分拷贝至新字符串中
			memcpy(newStrData, str->data, found - str->data);
			// 将newSubStr拷贝到新字符串中
			memcpy(newStrData + (found - str->data), newSubStr, newSubStrLen);
			// 将基础字符串str中oldSubStr末尾的部分拷贝到新字符串中
			memcpy(newStrData + (found - str->data) + newSubStrLen, found + oldSubStrLen, str->length - (found - str->data) - oldSubStrLen);
			// 为新字符串结尾添加'\0'
			newStrData[newStrLen] = '\0';

			// 在释放原字符串内存的前提下,更新str的data,length和capacity
			free(str->data);
			str->data = newStrData;
			str->length = newStrLen;
			str->capacity = newStrLen + 1;

			// 返回更新后的字符串str
			return str;			
		}
	}
	return NULL;
}


// 替换字符串中的指定字符
Str* replaceChar(Str* str, char oldChar, char newChar) {
	if (NULL != str)
	{
		// 循环控制条件为长度,而不是容量,因为处理的是有内容的字符
		for (int i = 0; i < str->length; i++) {
			if (str->data[i] == oldChar) {
				str->data[i] = newChar;
			}
		}
		str->data[str->capacity] = '\0';  // 添加'\0'到新字符串的末尾
		return str;
	}
	return NULL;
}


// 使用 isalpha()函数来检查输入的字符是否为字母,
// 是,使用 toupper() 函数,如果不是,不变。

// 将字符串全部转换为大写
Str* toUpperCase(Str* str) {
	for (int i = 0; i < str->length; i++) {
		if (isalpha(str->data[i])) {  
			str->data[i] = toupper(str->data[i]);
		}
	}
	//str->data[str->length] = '\0';
	return str;
}

// 将字符串转换为小写
Str* toLowerCase(Str* str) {
	for (int i = 0; i < str->length; i++) {
		if (isalpha(str->data[i])) {
			str->data[i] = tolower(str->data[i]);
		}
	}
	str->data[str->length] = '\0';
	return str;
}


// 测试用例
int main(int argc, char* argv[]) {
	
	// 测试创建Str对象
	Str* str = createStrByData(&str,"Hello World!");
	assert(str != NULL); // 断言
	printf("str addr 0x%p\n", str);

	// 测试计算字符串长度
	assert(getStrLength(str) == 12);
	assert(strcmp(str->data, "Hello World!") == 0);

	// 测试删除Str对象
	deleteStr(&str);
	assert(str == NULL);

	// 测试字符串子串截取
	str = createStrByData(&str, "Hello World!");
	Str* substr = subStr(str, 6, 5);
	assert(substr != NULL);
	assert(substr->length == 5);
	assert(strcmp(substr->data, "World") == 0);
	deleteStr(&substr);

	// 测试字符串子串截取(越界)
	substr = subStr(str, 13, 1);
	assert(substr == NULL);

	// 测试字符串子串截取(!)
	substr = subStr(str, 11, 1);
	assert(substr != NULL);
	assert(substr->length != 0);
	deleteStr(&substr);

	// 测试查找子字符串
	int index = findSubStr(str, "World");
	assert(index == 6);

	// 测试查找子字符串(不存在)
	index = findSubStr(str, "Goodbye");
	assert(index == -1);

	// 测试查找子字符串(空字符串)
	index = findSubStr(str, "");
	assert(index == 0);

	// 测试将字符串转换为小写
	initStrByData(str, "HELLO WORLD!");  // 再次初始化str
	toLowerCase(str);
	assert(strcmp(str->data, "hello world!") == 0);

	// 测试替换字符
	replaceChar(str, 'o', 'x');
	assert(strcmp(str->data, "hellx wxrld!") == 0);

	// 测试释放Str对象内存
	deleteStr(&str);         // bug,程序卡死
	printf("del:str add 0x%p\n", str);

	pause;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值