第 4 章 串(文本行编辑实现)

news2024/10/7 3:17:51

1. 背景说明

该文本编辑器利用串的堆实现,其中对串的原始存储方式基本不作修改(有部分修改之处),优化之处在于在串的末尾加上了一个空字符,目的是区分字符串结尾,便于将串保存在文件中,且该优化不对原来的功能造成影响,利用了二者的优点。

关键点在于从文件中读出字符串将其保存在串中和将串保存在文件中的处理方式,其实现原理图如下。

2. 示例代码

1) status.h

/* DataStructure 预定义常量和类型头文件 */

#ifndef STATUS_H
#define STATUS_H

#define CHECK_NULL(pointer) if (!(pointer)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_PTR); \
	return NULL; \
}

#define CHECK_RET(ret) if (ret != RET_OK) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ret); \
	return ret; \
}

#define CHECK_VALUE(value, ERR_CODE) if (value) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return ERR_CODE; \
}

#define CHECK_FALSE(value, ERR_CODE) if (!(value)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return FALSE; \
} 

/* 函数结果状态码 */
#define TRUE 					1			/* 返回值为真 */
#define FALSE 					0			/* 返回值为假 */
#define RET_OK 					0			/* 返回值正确 */
#define INFEASIABLE    		   	2			/* 返回值未知 */
#define ERR_MEMORY     		   	3			/* 访问内存错 */
#define ERR_NULL_PTR   			4			/* 空指针错误 */
#define ERR_MEMORY_ALLOCATE		5			/* 内存分配错 */
#define ERR_NULL_STACK			6			/* 栈元素为空 */
#define ERR_PARA				7			/* 函数参数错 */
#define ERR_OPEN_FILE			8			/* 打开文件错 */
#define ERR_NULL_QUEUE			9			/* 队列为空错 */
#define ERR_FULL_QUEUE			10			/* 队列为满错 */
#define ERR_NOT_FOUND			11			/* 表项不存在 */
typedef int Status;							/* Status 是函数的类型,其值是函数结果状态代码,如 RET_OK 等 */
typedef int Bollean;						/* Boolean 是布尔类型,其值是 TRUE 或 FALSE */

#endif // !STATUS_H

2)  hString.h

/* 串的堆分配存储实现头文件 */

#ifndef HSTRING_H
#define HSTRING_H

#include "status.h"

typedef struct {
	char *ch;
	int length;
} HString;

/* 初始化(产生空串)字符串 T */
void InitString(HString *T);

/* 生成一个其值等于串常量 chars 的串 T */
Status StrAssign(const char *chars, HString *T);

/* 初始条件: 串 S 存在
   操作结果: 由串 S 复制得串 T */
Status StrCopy(const HString *S, HString *T);

/* 初始条件: 串 S 存在
   操作结果: 若 S 为空串,则返回 TRUE,否则返回 FALSE */
Bollean StrEmpty(const HString *S);

/* 若 S > T,则返回值 > 0;若 S = T,则返回值 = 0;若 S < T,则返回值 < 0 */
int StrCompare(const HString *S, const HString *T);

/* 返回 S 的元素个数,称为串的长度 */
int StrLength(const HString *S);

/* 将 S 清为空串 */
Status ClearString(HString *S);

/* 用 T 返回由 S1 和 S2 联接而成的新串 */
Status Concat(const HString *S1, const HString *S2, HString *T);

/* 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串
   其中,1 ≤ pos ≤ StrLength(S) 且 0 ≤ len ≤ StrLength(S) - pos + 1 */
Status SubString(const HString *S, int pos, int len, HString *Sub);

/* 算法 4.1,T 为非空串。若主串 S 中第 pos 个字符之后存在与 T 相等的子串
   则返回第一个这样的子串在 S 中的位置,否则返回 0 */
int Index(const HString *S, const HString *T, int pos);

/* 算法 4.4, 在串 S 的第 pos 个字符之前插入串 T, 1 ≤ pos ≤ StrLength(S) + 1 */
Status StrInsert(const HString *T, int pos, HString *S);

/* 从串 S 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *S);

/* 初始条件: 串 S, T 和 V 存在, T 是非空串(此函数与串的存储结构无关)
   操作结果: 用 V 替换主串 S 中出现的所有与 T 相等的不重叠的子串 */
Status Replace(const HString *T, const HString *V, HString *S);

/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void);

/* 输出 T 字符串 */
void StrPrint(const HString *T);

#endif // !HSTRING_H

3) hString.c

/* 串的堆分配存储实现源文件 */

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

/* 初始化(产生空串)字符串 T */
void InitString(HString *T)
{
	T->length = 0;
	T->ch = NULL;
}

/* 生成一个其值等于串常量 chars 的串 T */
Status StrAssign(const char *chars, HString *T)
{
	CHECK_VALUE(!chars, ERR_NULL_PTR);
	if (T->ch) {
		free(T->ch);
	}

	int length = (int)strlen(chars);
	if (length == 0) {
		T->ch = NULL;
		T->length = 0;
		return RET_OK;
	}

	T->ch = (char *)malloc(sizeof(char) * (unsigned long long )(length + 1));
	CHECK_VALUE(!(T->ch), ERR_MEMORY_ALLOCATE);
	T->length = length;
	T->ch[length] = '\0';
	for (int i = 0; i < length; ++i) {
		T->ch[i] = chars[i];
	}

	return RET_OK;
}

/* 初始条件: 串 S 存在
   操作结果: 由串 S 复制得串 T */
Status StrCopy(const HString *S, HString *T)
{
	CHECK_VALUE(!S || !T, ERR_NULL_PTR);
	if (T->ch) {
		free(T->ch);
	}

	T->ch = (char *)malloc(sizeof(char) * (unsigned long long)(S->length + 1));
	CHECK_VALUE(!(T->ch), ERR_MEMORY_ALLOCATE);
	T->length = S->length;
	T->ch[S->length] = '\0';
	for (int i = 0; i < S->length; ++i) {
		T->ch[i] = S->ch[i];
	}

	return RET_OK;
}

/* 初始条件: 串 S 存在
   操作结果: 若 S 为空串,则返回 TRUE,否则返回 FALSE */
Bollean StrEmpty(const HString *S)
{
	return ((S->length == 0) && (S->ch == NULL)) ? TRUE : FALSE;
}

/* 若 S > T,则返回值 > 0;若 S = T,则返回值 = 0;若 S < T,则返回值 < 0 */
int StrCompare(const HString *S, const HString *T)
{
	CHECK_VALUE(!S || !T, ERR_NULL_PTR);
	for (int i = 0; i < S->length && i < T->length; ++i) {
		if (S->ch[i] != T->ch[i]) {
			return S->ch[i] - T->ch[i];
		}
	}

	return S->length - T->length;
}

/* 返回 S 的元素个数,称为串的长度 */
int StrLength(const HString *S)
{
	return S->length;
}

/* 将 S 清为空串 */
Status ClearString(HString *S)
{
	if (S->ch) {
		free(S->ch);
		S->ch = NULL;
	}

	S->length = 0;

	return RET_OK;
}

/* 用 T 返回由 S1 和 S2 联接而成的新串 */
Status Concat(const HString *S1, const HString *S2, HString *T)
{
	CHECK_VALUE(!S1 || !S2 || !T, ERR_NULL_PTR);
	if (T->ch) {
		free(T->ch);
	}

	int length = S1->length + S2->length;
	T->ch = (char *)malloc(sizeof(char) * length);
	CHECK_VALUE(!(T->ch), ERR_MEMORY_ALLOCATE);
	for (int i = 0; i < S1->length; ++i) {
		T->ch[i] = S1->ch[i];
	}

	for (int i = 0; i < S2->length; ++i) {
		T->ch[S1->length + i] = S2->ch[i];
	}

	T->length = length;

	return RET_OK;
}

/* 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串
   其中,1 ≤ pos ≤ StrLength(S) 且 0 ≤ len ≤ StrLength(S) - pos + 1 */
Status SubString(const HString *S, int pos, int len, HString *Sub)
{
	CHECK_VALUE(!S || !Sub, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > S->length) || (len < 0) || (len > S->length - pos + 1), ERR_PARA);
	if (Sub->ch) {
		free(Sub->ch);
	}

	if (len == 0) {
		Sub->ch = NULL;
		Sub->length = 0;
	} else {
		Sub->ch = (char *)malloc(sizeof(char) * (unsigned long long)(len + 1));
		CHECK_VALUE(!(Sub->ch), ERR_MEMORY_ALLOCATE);
		for (int i = 0; i < len; ++i) {
			Sub->ch[i] = S->ch[pos - 1 + i];
		}

		Sub->length = len;
		Sub->ch[Sub->length] = '\0';
	}

	return RET_OK;
}

/* 算法 4.1,T 为非空串。若主串 S 中第 pos 个字符之后存在与 T 相等的子串
   则返回第一个这样的子串在 S 中的位置,否则返回 0 */
int Index(const HString *S, const HString *T, int pos)
{
	int maxRange = StrLength(S) - StrLength(T) + 1;
	CHECK_VALUE((pos < 1) || (pos > maxRange), 0);
	HString sub;
	InitString(&sub);
	while (pos <= maxRange) {
		SubString(S, pos, StrLength(T), &sub);
		if (StrCompare(&sub, T) != 0) {
			++pos;
		} else {
			return pos;
		}
	}

	return 0;
}

/* 算法 4.4, 在串 S 的第 pos 个字符之前插入串 T, 1 ≤ pos ≤ StrLength(S) + 1 */
Status StrInsert(const HString *T, int pos, HString *S)
{
	CHECK_VALUE(!T || !S, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > S->length + 1), ERR_PARA);
	if (T->length == 0) {
		return RET_OK;
	}

	S->ch = (char *)realloc(S->ch, sizeof(char) * (unsigned long long)(S->length + T->length + 1));
	CHECK_VALUE(!(S->ch), ERR_MEMORY_ALLOCATE);
	for (int i = S->length - 1; i >= pos - 1; --i) {
		S->ch[i + T->length] = S->ch[i];
	}

	S->length += T->length;
	for (int i = 0; i < T->length; ++i) {
		S->ch[pos - 1 + i] = T->ch[i];
	}

	S->ch[S->length] = '\0';

	return RET_OK;
}

/* 从串 S 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);
	CHECK_VALUE(pos - 1 + len > S->length, ERR_PARA);
	for (int i = pos - 1; i < S->length - len; ++i) {
		S->ch[i] = S->ch[i + len];
	}

	S->length -= len;
	S->ch = (char *)realloc(S->ch, sizeof(char) * (unsigned long long)(S->length + 1));
	S->ch[S->length] = '\0';

	return RET_OK;
}

/* 初始条件: 串 S, T 和 V 存在, T 是非空串(此函数与串的存储结构无关)
   操作结果: 用 V 替换主串 S 中出现的所有与 T 相等的不重叠的子串 */
Status Replace(const HString *T, const HString *V, HString *S)
{
	CHECK_VALUE(!T || !V || !S, ERR_NULL_PTR);
	int pos = 1;
	do {
		pos = Index(S, T, pos);
		if (pos) {
			StrDelete(pos, StrLength(T), S);
			StrInsert(V, pos, S);
			pos += StrLength(V);
		}
	} while (pos);

	return RET_OK;
}

/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void)
{
	printf("Do not need to destroy!\n");
}

/* 输出 T 字符串 */
void StrPrint(const HString *T)
{
	for (int i = 0; i < T->length; ++i) {
		putchar(T->ch[i]);
	}
}

4) textEditing.h

/* 文本行编辑实现头文件 */

#ifndef TEXTEDITING_H
#define TEXTEDITING_H

#include "hString.h"

#define MAX_LINE_NUM 25
#define MAX_CHAR_NUM 75
#define NAME_LEN 20

/* 显示文件内容 */
void ShowText(int lineNum, const HString *T);

/* 插入新行 */
Status InsertNewLine(int *lineNum, HString *T);

/* 删除内容 */
Status DeleteContent(int *lineNum, HString *T);

/* 拷贝文件内容 */
Status CopyContent(int *lineNum, HString *T);

/* 修改文本行 */
Status ModifyContent(int lineNum, HString *T);

/* 查找字符串 */
Status SearchContent(int lineNum, const HString *T);

/* 替换字符串 */
Status ReplaceContent(int lineNum, HString *T);

/* 将文本行同步到文件中 */
Status SaveToFile(int nameLength, const char *fileName, int lineNum, const HString *T);

/* 显示主菜单 */
void ShowMainMenu(void);

/* 显示子菜单 */
void ShowSubMenu(void);

/* 初始化 */
Status MainMenu(int nameLength, int maxLine, char *fileName, int *lineNum, HString *T);

#endif // !TEXTEDITING_H

5) textEditing.c

/* 文本行编辑实现源文件 */

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

/* 获取最多 n - 1 个字符保存在 str 指向的内存中 */
static char *SGets(int n, char *str)
{
	char *retVal = fgets(str, n, stdin);
	if (retVal) {
		char *find = strchr(str, '\n');
		if (find) {
			*find = '\0';
		} else {
			while (getchar() != '\n') {
				continue;
			}
		}
	}

	return retVal;
}

/* 打开已存在文件或创建新文件 */
static Status OperateFile(int nameLength, const char *fileName)
{
	CHECK_VALUE(!fileName, ERR_NULL_PTR);
	CHECK_VALUE(nameLength > NAME_LEN, ERR_PARA);
	FILE *fp;
	errno_t err_ret = fopen_s(&fp, fileName, "ab+");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	fclose(fp);

	return RET_OK;
}

/* 从文件中读取文本行 */
static Status ReadFromFile(int nameLength, const char *fileName, int *lineNum, HString *T)
{
	CHECK_VALUE(!fileName || !T, ERR_NULL_PTR);
	CHECK_VALUE((nameLength > NAME_LEN), ERR_PARA);
	FILE *fp;
	errno_t err_ret = fopen_s(&fp, fileName, "rb");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	char str[MAX_CHAR_NUM + 1];
	*lineNum = 0;
	Status ret = RET_OK;
	while ((fgets(str, MAX_CHAR_NUM + 1, fp) != NULL) && (*lineNum <= MAX_LINE_NUM)) {
		str[strlen(str) - 1] = '\0';
		ret = StrAssign(str, T + *lineNum);
		if (ret != RET_OK) {
			fclose(fp);
			break;
		}

		++(*lineNum);
	}

	CHECK_VALUE(ret != RET_OK, ret);
	fclose(fp);

	return RET_OK;
}

/* 显示文件内容 */
void ShowText(int lineNum, const HString *T)
{
	for (int i = 0; i < lineNum; ++i) {
		printf("Line %d: ", i + 1);
		StrPrint(T + i);
		putchar('\n');
	}
}

/* 在第 pos 行前插 num 行 */
static Status InsertLine(int pos, int num, const char str[][MAX_CHAR_NUM], HString *T, int *lineNum)
{
	CHECK_VALUE((*lineNum + num > MAX_LINE_NUM) || ((pos < 1) || (pos > (*lineNum + 1))), ERR_PARA);
	Status ret = RET_OK;
	for (int i = *lineNum - 1; i >= pos - 1; --i) {
		ret = StrCopy(T + i, T + i + num);
		CHECK_VALUE(ret != RET_OK, ret);
	}

	for (int i = 0; i < num; ++i) {
		CHECK_VALUE(strlen(str[i]) > MAX_CHAR_NUM, ERR_PARA);
		StrAssign(str[i], T + pos - 1 + i);
		++(*lineNum);
	}

	return RET_OK;
}

/* 插入新行 */
Status InsertNewLine(int *lineNum, HString *T)
{
	printf("Please input the position(Line Before) you want to insert and the line num: ");
	int pos, num;
	scanf_s("%d%d", &pos, &num);
	getchar();
	CHECK_VALUE((*lineNum + num > MAX_LINE_NUM) || ((pos < 1) || (pos > (*lineNum + 1))), ERR_PARA);
	char str[MAX_LINE_NUM][MAX_CHAR_NUM] = { 0 };
	for (int i = 0; i < num; ++i) {
		printf("Please input the %dth string: ", i + 1);
		SGets(MAX_CHAR_NUM, str[i]);
	}

	Status ret = InsertLine(pos, num, str, T, lineNum);
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

/* 删除文本行 */
static Status DeleteLine(int pos, int num, HString *T, int *lineNum)
{
	CHECK_VALUE(!T || !lineNum, ERR_NULL_PTR);
	CHECK_VALUE((pos <= 0) || (pos > *lineNum - num + 1), ERR_PARA);
	for (int i = pos - 1 + num; i < *lineNum; ++i) {
		StrCopy(T + i, T + i - num);
	}
	
	*lineNum -= num;

	return RET_OK;
}

/* 删除内容 */
Status DeleteContent(int *lineNum, HString *T)
{
	printf("Please input the pos(From) and num you want to delete: ");
	int pos, num;
	scanf_s("%d%d", &pos, &num);
	getchar();
	Status ret = DeleteLine(pos, num, T, lineNum);
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

/* 把第 pos 行开始的 num 行插在原 insPos 行之前 */
static Status CopyLine(int pos, int num, int insPos, HString *T, int *lineNum)
{
	CHECK_VALUE(!T || !lineNum, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > *lineNum - num + 1) || (insPos < 1) || (insPos > *lineNum + 1)
		|| (*lineNum + num > MAX_LINE_NUM), ERR_PARA);
	char str[MAX_LINE_NUM][MAX_CHAR_NUM] = { 0 };
	for (int i = 0; i < num; ++i) {
		(void)strcpy_s(str[i], MAX_CHAR_NUM, (T + pos - 1 + i)->ch);
	}

	Status ret = InsertLine(insPos, num, str, T, lineNum);
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

/* 拷贝文件内容 */
Status CopyContent(int *lineNum, HString *T)
{
	CHECK_VALUE(!T || !lineNum, ERR_NULL_PTR);
	printf("Please input the pos and num and insPos: ");
	int pos, num, insPos;
	scanf_s("%d%d%d", &pos, &num, &insPos);
	getchar();
	Status ret = CopyLine(pos, num, insPos, T, lineNum);
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

static ModifyLine(const char *str, int pos, HString *T)
{
	Status ret = StrAssign(str, T + pos - 1);
	CHECK_VALUE(ret != RET_OK, ret);

	return ret;
}

/* 修改文本行 */
Status ModifyContent(int lineNum, HString *T)
{
	CHECK_VALUE(!T, ERR_NULL_PTR);
	printf("Please input the pos you want to modify: ");
	int pos;
	scanf_s("%d", &pos);
	getchar();
	CHECK_VALUE((pos < 1) || (pos > lineNum), ERR_PARA);
	printf("Please input the content you want to fill: ");
	char str[MAX_CHAR_NUM];
	SGets(MAX_CHAR_NUM, str);
	Status ret = ModifyLine(str, pos, T);
	CHECK_VALUE(ret != RET_OK, ret);

	return ret;
}

static Status SearchString(const char *str, int lineNum, const HString *T)
{
	HString s;
	InitString(&s);
	StrAssign(str, &s);
	Bollean continueFind = TRUE;
	for (int i = 0; (i < lineNum) && continueFind; ++i) {
		int start = 1;
		while (start) {
			start = Index(T + i, &s, start);
			if (start) {
				printf("Find in Line %d: ", i + 1);
				StrPrint(T + i);
				printf(" ,Find in %dth position\n", start);
				printf("Continue find?(Y/N) ");
				if (getchar() != 'Y') {
					getchar();
					continueFind = FALSE;
					break;
				}

				++start;
				getchar();
			}
		}
	}

	if (continueFind) {
		printf("Not find!\n");
	}

	return RET_OK;
}

/* 查找字符串 */
Status SearchContent(int lineNum, const HString *T)
{
	CHECK_VALUE(!T, ERR_NULL_PTR);
	printf("Please input the string you want to find: ");
	char str[MAX_CHAR_NUM] = { 0 };
	SGets(MAX_CHAR_NUM, str);
	Status ret = SearchString(str, lineNum, T);
	CHECK_VALUE(ret != RET_OK, ret);

	return ret;
}

static Status ReplaceString(const char *str, const char *strInstead, int lineNum, HString *T)
{
	HString s, sInstead;
	InitString(&s);
	InitString(&sInstead);
	StrAssign(str, &s);
	StrAssign(strInstead, &sInstead);
	int lengthChange = (int)(strlen(strInstead) - strlen(str));
	Bollean continueFind = TRUE;
	for (int i = 0; (i < lineNum) && continueFind; ++i) {
		int start = 1;
		while (start) {
			start = Index(T + i, &s, start);
			if (start) {
				CHECK_VALUE(StrLength(T + i) + lengthChange > MAX_CHAR_NUM, ERR_PARA);
				printf("Find in Line %d: ", i + 1);
				StrPrint(T + i);
				printf(" ,Find in %dth position\n", start);
				StrDelete(start, (int)strlen(str), T + i);
				StrInsert(&sInstead, start, T + i);
				printf("Continue replace?(Y/N) ");
				if (getchar() != 'Y') {
					getchar();
					continueFind = FALSE;
					break;
				}

				++start;
				getchar();
			}
		}
	}

	if (continueFind) {
		printf("Not find!\n");
	}

	return RET_OK;
}

/* 替换字符串 */
Status ReplaceContent(int lineNum, HString *T)
{
	CHECK_VALUE(!T, ERR_NULL_PTR);
	printf("Please input the string you want to replace: ");
	char str[MAX_CHAR_NUM] = { 0 };
	SGets(MAX_CHAR_NUM, str);
	printf("Please input the string you want to instead: ");
	char strInstead[MAX_CHAR_NUM] = { 0 };
	SGets(MAX_CHAR_NUM, strInstead);
	Status ret = ReplaceString(str, strInstead, lineNum, T);
	CHECK_VALUE(ret != RET_OK, ret);

	return ret;
}

/* 将文本行同步到文件中 */
Status SaveToFile(int nameLength, const char *fileName, int lineNum, const HString *T)
{
	CHECK_VALUE(!fileName || !T, ERR_NULL_PTR);
	CHECK_VALUE((nameLength > NAME_LEN) || (lineNum > MAX_LINE_NUM), ERR_PARA);
	FILE *fp;
	errno_t err_ret = fopen_s(&fp, fileName, "wb");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	char *str = NULL;
	for (int i = 0; i < lineNum; ++i) {
		fputs(T[i].ch, fp);
		fputc(10, fp);
	}

	fclose(fp);

	return RET_OK;
}

/* 显示主菜单 */
void ShowMainMenu(void)
{
	printf("Main Menu:\n");
	printf("1. Open file(New or Existed)\n");
	printf("0. Exit system\n");
}

/* 显示子菜单 */
void ShowSubMenu(void)
{
	printf("Sub Menu:\n");
	printf("1. Show text\n");
	printf("2. Insert new line\n");
	printf("3. Delete line\n");
	printf("4. Copy existed line\n");
	printf("5. Modify existed line\n");
	printf("6. Find string\n");
	printf("7. Replace string\n");
	printf("8. Save to file\n");
	printf("0. Exit to main menu\n");
}

/* 初始化 */
Status MainMenu(int nameLength, int maxLine, char *fileName, int *lineNum, HString *T)
{
	CHECK_VALUE(!fileName || !lineNum || !T, ERR_NULL_PTR);
	CHECK_VALUE((nameLength > NAME_LEN) || (maxLine > MAX_LINE_NUM), ERR_PARA);
	printf("Please input the file name: ");
	SGets(NAME_LEN, fileName);
	OperateFile(nameLength, fileName);
	ReadFromFile(nameLength, fileName, lineNum, T);
	ShowText(*lineNum, T);

	return RET_OK;
}

6) main.c

/* 入口程序源文件 */

#include "textEditing.h"
#include <stdio.h>
#include <string.h>

int main(void)
{
	char fileName[NAME_LEN] = { 0 };
	HString T[MAX_LINE_NUM] = { 0 };
	for (int i = 0; i < MAX_LINE_NUM; ++i) {
		InitString(T + i);
	}

	int lineNum = 0;
	int mainChoose = 1, subChoose = 1;
	while (mainChoose) {
		printf("\n");
		ShowMainMenu();
		printf("Please input you choose: ");
		scanf_s("%d", &mainChoose);
		getchar();
		switch (mainChoose) {
			case 1:
				MainMenu((int)strlen(fileName), MAX_LINE_NUM, fileName, &lineNum, T);
				subChoose = 1;
				while (subChoose) {
					printf("\n");
					ShowSubMenu();
					printf("Please input your choose: ");
					scanf_s("%d", &subChoose);
					getchar();
					switch (subChoose) {
						case 1:
							ShowText(lineNum, T);
							break;
						case 2:
							InsertNewLine(&lineNum, T);
							break;
						case 3:
							DeleteContent(&lineNum, T);
							break;
						case 4:
							CopyContent(&lineNum, T);
							break;
						case 5:
							ModifyContent(lineNum, T);
							break;
						case 6:
							SearchContent(lineNum, T);
							break;
						case 7:
							ReplaceContent(lineNum, T);
							break;
						case 8:
							SaveToFile((int)strlen(fileName), fileName, lineNum, T);
							break;
						case 0:
							break;
					}
				}

				break;
			case 0:
				break;
		}
	}

	for (int i = 0; i < MAX_LINE_NUM; ++i) {
		ClearString(T + i);
	}

	return 0;
}

3. 运行示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1038493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript入门——(2)基础语法(上)

一、JavaScript介绍 1.1 JavaScript是什么 1.1.1 JavaScript是什么&#xff1f; JavaScript是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互效果。 注意&#xff1a;HTML和CSS是标记语言。 1.1.2 作用&#xff08;做什么&#xff1f…

8月最新修正版风车IM即时聊天通讯源码+搭建教程

8月最新修正版风车IM即时聊天通讯源码搭建教程。风车 IM没啥好说的很多人在找,IM的天花板了,知道的在找的都知道它的价值,开版好像就要29999,后端加密已解,可自己再加密,可反编译出后端项目源码,已增加启动后端需要google auth双重验证,pc端 web端 wap端 android端 ios端 都有 …

小米机型解锁bl 跳“168小时”限制 操作步骤分析

写到前面的安全提示 了解解锁bl后的风险&#xff1a; 解锁设备后将允许修改系统重要组件&#xff0c;并有可能在一定程度上导致设备受损&#xff1b;解锁后设备安全性将失去保证&#xff0c;易受恶意软件攻击&#xff0c;从而导致个人隐私数据泄露&#xff1b;解锁后部分对系…

老胡的周刊(第109期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 lobe-chat[2] LobeChat 是一个开源的、可扩展…

面试题:说说Java并发运行中的一些安全问题

文章目录 1.什么是多线程并发运行安全问题&#xff1f;2.用synchronized修饰的方法3.同步块4.使用Synchronized修饰静态方法5.互斥锁6.死锁现象7.wait()和sleep()的区别 1.什么是多线程并发运行安全问题&#xff1f; 当多个线程并发操作一个数据时&#xff0c;由于线程操作的时…

在Linux上安装Percona Toolkit工具

安装步骤 1. 下载安装包 下载地址&#xff1a;https://www.percona.com/software/database-tools/percona-toolkit 2.上传并解压 上传tar包到服务器&#xff0c;并通过tar -zxvf 文件名.tar.gz解压。工具在bin文件夹中&#xff0c;这个是免安装的。 3. 配置环境变量 配置…

安装OpenSearch

title: “安装opensearch” createTime: 2021-11-30T19:13:4508:00 updateTime: 2021-11-30T19:13:4508:00 draft: false author: “name” tags: [“es”,“安装”] categories: [“OpenSearch”] description: “测试的” 说明 基于Elasticsearch7.10.2 的 opensearch-1.1.…

gRPC之实现TLS通信加密_已设置图床

gRPC之实现TLS通信加密 "crypto/tls"包 “crypto/tls” 是 Go 编程语言中的一个包&#xff0c;用于实现 TLS&#xff08;传输层安全&#xff09;协议。TLS 协议用于加密和保护网络通信&#xff0c;通常用于保护敏感数据的传输&#xff0c;如密码、支付信息等。在 G…

详解--计算机存储相关(寄存器、CPU Cache、内存、外存)

CPU寄存器、高速缓冲存储器、主存储器、外存储器 1. 主存储器 参考链接–主存 参考链接–内存 主存储器简称 主存&#xff0c;又称 内存储器&#xff08;简称 内存&#xff09;。作用 暂时存放CPU中的运算数据。存放指令和数据&#xff0c;并能由中央处理器&#xff08;CPU&a…

什么是Service Worker?它在PWA中的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Service Worker的作用是什么&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

1066 二级C语言-自定义函数

输入一个正数x和一个正整数n&#xff0c;求下列算式的值。要求定义两个调用函数&#xff1a; &#xff08;1&#xff09;fact(n)计算n的阶乘&#xff1b; &#xff08;2&#xff09;mypow(x,n)计算x的n次幂&#xff08;即xn&#xff09;&#xff0c;两个函数的返回值类型是do…

HEC-HMS和HEC-RAS水文模型、防洪评价报告编制及洪水建模、洪水危险性评价等相关案例解析

► HEC-RAS一维、二维建模方法及应用 【目标】&#xff1a; 1.掌握一维数学模型基本地形导入方法 2.掌握恒定流、非恒定流一维数学模型水流计算方法 3.掌握一维数学模型计算结果分析&#xff0c;水面线成果分析及调试&#xff1b;流速分布图输出方法 4.掌握一维数学模型增设构…

如何让一个uniform variable在多级shader中都起作用(类似C语言的全局变量)?

GLSL编程中通常设计多个shader&#xff0c;如vertex shader, fragment shader等等。在最近的某个项目中&#xff0c;我需要定义一个变量&#xff0c;该变量类似C语言中的全局变量&#xff0c;要同时在两个shader中都起作用。c - OpenGL Uniform Across Multiple Shaders - Stac…

2023-9-23 区间选点

题目链接&#xff1a;区间选点 #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n;struct Range {int l, r;bool operator< (const Range &W) const{return r < W.r;} }range[N];int main() {scanf("%d"…

MyBatisPlus + ShardingJDBC 批量插入不返回主键ID

本文讲述一个由 ShardingJDBC 使用不当引起的悲惨故事。 一. 问题重现 有一天运营反馈我们部分订单状态和第三方订单状态无法同步。 根据现象找到了不能同步订单状态是因为 order 表的 thirdOrderId 为空导致的&#xff0c;但是这个字段为啥为空&#xff0c;排查过程比较波折…

NebulaGraph实战:2-NebulaGraph手工和Python操作

图数据库是专门存储庞大的图形网络并从中检索信息的数据库。它可以将图中的数据高效存储为点&#xff08;Vertex&#xff09;和边&#xff08;Edge&#xff09;&#xff0c;还可以将属性&#xff08;Property&#xff09;附加到点和边上。本文以示例数据集basketballplayer为例…

java的Map和Set集合

Set集合 一.HashSet HashSet 元素是无序的 向Hashset中添加元素时&#xff0c;是如何判断元素是否重复的: 添加元素时&#xff0c;如果用equals判断效率太低&#xff0c;因为equals是一个一个字符比较 HashSet底层用到hashCode和equals 一个内容&#xff1a;"sahdihwo&q…

FPGA 安装Quartus 13.1无法生成.sof文件

FPGA 安装Quartus 13.1无法生成.sof文件 安装环境编译无法生成 .sof文件分析原因 找资料1.第1篇文章2.第2篇文章 安装环境 Quarter II 13.0下载、安装、破解包括可能出现的几乎所有的问题详解野火FPGA安装视频 编译无法生成 .sof文件 分析原因 1.推测可能是破解失败。2.安装…

洛谷bfs题2---P1825 [USACO11OPEN] Corn Maze S

P1825 [USACO11OPEN] Corn Maze S import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {public static int N;//行public static int M;//列public static Queue<Integer> q new LinkedList<>();public static in…

变量、因子、缺失值、类型转换、剔除多余变量、随机抽样、用R使用SQL、trim、na.rm=TRUE、数据标准化应用

变量&#xff1a;名义型、有序型、连续型变量 名义型&#xff1a;普通事件类型&#xff0c;如糖尿病I型和糖尿病II型。 有序型&#xff1a;有顺序的事件类型&#xff0c;如一年级、二年级和三年级。 连续型&#xff1a;表示有顺序的数量&#xff0c;如年龄。 因子&#xff1a;…