C语言小说编辑器:源码注释详解
C语言小说编辑器:源码注释详解
这篇博客分享一个使用 C 语言实现的小说编辑器,并对源码进行了详细的注释,方便大家理解。
代码实现c#include <stdio.h>#include <stdlib.h>#include <string.h>
// 定义最大字符数和页面大小#define MAX_SIZE 50#define PAGE_SIZE 20
// 结点定义typedef struct Node { struct Node* prev; // 指向前一个结点的指针 struct Node* next; // 指向后一个结点的指针 char content[MAX_SIZE]; // 存放段落内容的数组 int count; // 当前结点存放的字符数量} Node;
// 小说定义typedef struct Novel { Node* head; // 指向链表头部的指针 Node* tail; // 指向链表尾部的指针 int numPages; // 小说的页数 int currentPage; // 当前页数} Novel;
// 创建新结点Node* createNode() { Node* newNode = (Node*)malloc(sizeof(Node)); // 分配内存 newNode->prev = NULL; // 初始化前驱指针 newNode->next = NULL; // 初始化后继指针 newNode->count = 0; // 初始化字符计数 return newNode; // 返回新节点}
// 在指定结点后插入字符串void insertAfter(Node* node, const char* str) { int len = strlen(str); // 获取字符串长度 int i, j; // 遍历字符串 for (i = node->count, j = 0; i < MAX_SIZE && j < len; i++, j++) { node->content[i] = str[j]; // 将字符存入节点 } node->count = i; // 更新节点字符计数 // 如果字符串还有剩余 if (j < len) { Node* newNode = createNode(); // 创建新节点 // 将剩余字符存入新节点 for (i = 0; i < MAX_SIZE && j < len; i++, j++) { newNode->content[i] = str[j]; } newNode->count = i; // 更新新节点字符计数 newNode->prev = node; // 设置新节点的前驱指针 newNode->next = node->next; // 设置新节点的后继指针 if (node->next != NULL) { // 如果原节点有后继节点 node->next->prev = newNode; // 更新原节点后继节点的前驱指针 } node->next = newNode; // 更新原节点的后继指针 }}
// 从指定结点往后删除指定数量的字符void deleteChars(Node* node, int numChars) { int i; // 遍历要删除的字符数量 for (i = 0; i < numChars && node != NULL; i++) { node->count--; // 减少当前节点字符计数 // 如果当前节点字符计数小于0 if (node->count < 0) { node = node->prev; // 移动到前一个节点 if (node != NULL) { // 如果存在前一个节点 node->count--; // 减少前一个节点的字符计数 } } }}
// 合并指定结点和后面的结点void mergeNode(Node* node) { // 如果节点有后继节点 if (node->next != NULL) { Node* nextNode = node->next; // 获取后继节点 int i; // 遍历后继节点的字符 for (i = 0; i < nextNode->count; i++) { node->count++; // 增加当前节点字符计数 node->content[node->count] = nextNode->content[i]; // 将后继节点字符复制到当前节点 nextNode->count--; // 减少后继节点字符计数 // 如果当前节点已满 if (node->count == MAX_SIZE) { node->next = nextNode->next; // 将当前节点的后继指针指向后继节点的后继节点 if (nextNode->next != NULL) { // 如果后继节点还有后继节点 nextNode->next->prev = node; // 更新后继节点的后继节点的前驱指针 } free(nextNode); // 释放后继节点内存 break; } } }}
// 显示小说的所有内容void displayNovel(Novel* novel) { Node* node = novel->head; // 从头节点开始遍历 while (node != NULL) { // 遍历所有节点 int i; for (i = 0; i < node->count; i++) { // 遍历节点内容 printf('%c', node->content[i]); // 打印字符 } node = node->next; // 移动到下一个节点 } printf(' ');}
// 显示指定页的内容void displayPage(Novel* novel, int pageNo, int pageSize) { Node* node = novel->head; // 从头节点开始遍历 int count = 0; // 字符计数 // 找到指定页的起始节点 while (node != NULL && count < pageNo * pageSize) { count += node->count; // 累加字符计数 node = node->next; // 移动到下一个节点 } // 打印当前页内容 if (node != NULL) { int i, j; for (i = count - node->count, j = 0; j < pageSize && i < count; i++, j++) { printf('%c', node->content[i]); } printf(' '); }}
// 在小说中搜索指定字符串并计数int searchAndCount(Novel* novel, const char* str) { int count = 0; // 初始化计数器 Node* node = novel->head; // 从头节点开始遍历 // 遍历所有节点 while (node != NULL) { int i; // 遍历节点内容 for (i = 0; i < node->count; i++) { int j; // 比较字符串 for (j = 0; j < strlen(str); j++) { if (node->content[i + j] != str[j]) { break; } } if (j == strlen(str)) { // 找到匹配字符串 count++; // 计数器加一 } } node = node->next; // 移动到下一个节点 } return count; // 返回计数结果}
// 在小说中替换指定字符串void replaceString(Novel* novel, const char* str, const char* replacement) { Node* node = novel->head; // 从头节点开始遍历 // 遍历所有节点 while (node != NULL) { int i; // 遍历节点内容 for (i = 0; i < node->count; i++) { int j; // 比较字符串 for (j = 0; j < strlen(str); j++) { if (node->content[i + j] != str[j]) { break; } } // 找到匹配字符串 if (j == strlen(str)) { deleteChars(node, strlen(str)); // 删除原字符串 insertAfter(node, replacement); // 插入替换字符串 i += strlen(replacement) - 1; // 更新当前字符位置 } } node = node->next; // 移动到下一个节点 }}
// 将小说保存到文件void saveNovel(Novel* novel, const char* filename) { FILE* file = fopen(filename, 'w'); // 打开文件 if (file != NULL) { // 文件打开成功 Node* node = novel->head; // 从头节点开始遍历 while (node != NULL) { // 遍历所有节点 int i; for (i = 0; i < node->count; i++) { // 遍历节点内容 fputc(node->content[i], file); // 写入字符到文件 } node = node->next; // 移动到下一个节点 } fclose(file); // 关闭文件 printf('小说保存成功。 '); } else { printf('无法保存小说。 '); }}
// 从文件中加载小说void loadNovel(Novel* novel, const char* filename) { FILE* file = fopen(filename, 'r'); // 打开文件 if (file != NULL) { // 文件打开成功 Node* node = createNode(); // 创建新节点 novel->head = node; // 设置头节点 // 读取文件内容 while (!feof(file)) { char ch = fgetc(file); // 读取一个字符 if (ch == EOF) { // 到达文件末尾 break; } node->content[node->count] = ch; // 将字符存入节点 node->count++; // 增加节点字符计数 // 如果节点已满 if (node->count == MAX_SIZE) { Node* newNode = createNode(); // 创建新节点 node->next = newNode; // 设置当前节点的后继指针 newNode->prev = node; // 设置新节点的前驱指针 node = newNode; // 移动到新节点 } } novel->tail = node; // 设置尾节点 fclose(file); // 关闭文件 printf('小说加载成功。 '); } else { printf('无法加载小说。 '); }}
// 统计小说中的单词、数字和标点符号数量void countWordsDigitsPunctuation(Novel* novel) { int wordCount = 0; // 单词计数 int digitCount = 0; // 数字计数 int punctuationCount = 0; // 标点符号计数 Node* node = novel->head; // 从头节点开始遍历 // 遍历所有节点 while (node != NULL) { int i; // 遍历节点内容 for (i = 0; i < node->count; i++) { if (node->content[i] == ' ' || node->content[i] == ' ') { // 统计单词数量 wordCount++; } else if (node->content[i] >= '0' && node->content[i] <= '9') { // 统计数字数量 digitCount++; } else if (node->content[i] == '.' || node->content[i] == ',' || node->content[i] == ';' || node->content[i] == ':' || node->content[i] == '?' || node->content[i] == '!') { // 统计标点符号数量 punctuationCount++; } } node = node->next; // 移动到下一个节点 } printf('单词数量: %d ', wordCount); printf('数字数量: %d ', digitCount); printf('标点符号数量: %d ', punctuationCount);}
int main() { Novel novel; // 创建小说对象 novel.head = NULL; // 初始化头节点指针 novel.tail = NULL; // 初始化尾节点指针 novel.numPages = 0; // 初始化页数 novel.currentPage = 0; // 初始化当前页码
int choice; do { // 显示菜单 printf('
--- 小说编辑器 --- '); printf('1. 插入 '); printf('2. 删除 '); printf('3. 显示 '); printf('4. 显示页面 '); printf('5. 搜索和计数 '); printf('6. 替换 '); printf('7. 保存 '); printf('8. 加载 '); printf('9. 统计单词、数字和标点符号 '); printf('10. 退出 '); printf('请输入您的选择: '); scanf('%d', &choice); printf(' ');
char str[MAX_SIZE]; int pageNo, pageSize, numChars; char filename[100]; char replacement[MAX_SIZE];
// 根据用户选择执行相应操作 switch (choice) { case 1: // 插入 printf('请输入要插入的字符串: '); getchar(); fgets(str, MAX_SIZE, stdin); insertAfter(novel.tail, str); // 在尾部插入字符串 // 如果当前节点字符数量超过一半且存在下一个节点,则合并节点 if (novel.tail->count > MAX_SIZE / 2 && novel.tail->next != NULL) { mergeNode(novel.tail); } break; case 2: // 删除 printf('请输入要删除的字符数: '); scanf('%d', &numChars); deleteChars(novel.tail, numChars); // 从尾部删除字符 mergeNode(novel.tail); // 合并节点 break; case 3: // 显示全部内容 displayNovel(&novel); break; case 4: // 显示指定页面 printf('请输入页面编号: '); scanf('%d', &pageNo); printf('请输入页面大小: '); scanf('%d', &pageSize); displayPage(&novel, pageNo, pageSize); break; case 5: // 搜索并计数 printf('请输入要搜索的字符串: '); getchar(); fgets(str, MAX_SIZE, stdin); int count = searchAndCount(&novel, str); printf('计数: %d
', count); break; case 6: // 替换字符串 printf('请输入要替换的字符串: '); getchar(); fgets(str, MAX_SIZE, stdin); printf('请输入替换字符串: '); fgets(replacement, MAX_SIZE, stdin); replaceString(&novel, str, replacement); break; case 7: // 保存小说 printf('请输入要保存的文件名: '); scanf('%s', filename); saveNovel(&novel, filename); break; case 8: // 加载小说 printf('请输入要加载的文件名: '); scanf('%s', filename); loadNovel(&novel, filename); break; case 9: // 统计单词、数字和标点符号数量 countWordsDigitsPunctuation(&novel); break; case 10: // 退出程序 printf('正在退出... '); break; default: // 无效选择 printf('无效的选择,请重试。 '); break; } } while (choice != 10); // 用户选择退出程序则结束循环
return 0; }
代码说明
数据结构
小说编辑器使用链表存储文本内容,每个节点包含以下信息:
prev: 指向前一个节点的指针*next: 指向后一个节点的指针*content: 存储文本内容的字符数组*count: 当前节点存储的字符数量
函数功能
createNode(): 创建新节点并初始化*insertAfter(Node* node, const char* str): 在指定节点后插入字符串*deleteChars(Node* node, int numChars): 从指定节点开始删除指定数量的字符*mergeNode(Node* node): 合并指定节点和其后继节点*displayNovel(Novel* novel): 显示小说全部内容*displayPage(Novel* novel, int pageNo, int pageSize): 显示指定页面内容*searchAndCount(Novel* novel, const char* str): 搜索指定字符串并返回出现次数*replaceString(Novel* novel, const char* str, const char* replacement): 替换指定字符串*saveNovel(Novel* novel, const char* filename): 将小说内容保存到文件*loadNovel(Novel* novel, const char* filename): 从文件加载小说内容*countWordsDigitsPunctuation(Novel* novel): 统计小说中的单词、数字和标点符号数量
总结
这份代码简单易懂,适合初学者学习C语言链表和文件操作等知识。你也可以根据自己的需求对代码进行修改和扩展,例如添加更多编辑功能、实现更复杂的格式控制等。
原文地址: http://www.cveoy.top/t/topic/b8tk 著作权归作者所有。请勿转载和采集!