//本代码和所有注释由PYZ倾情编写
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <fstream>
#define char_num 1000 //一行的字符个数
using namespace std;
struct Node //节点,一个节点存一行的内容
{
char content[char_num] = {0}; //一行的内容字符数组
int LineNum;//行号
Node *next = nullptr;//后以个节点
Node *priv = nullptr;//前一个节点
};
class TextArea //包装节点的类;相当于一个TextArea类的对象就保存若干行的文字;但本程序实际上只需要存入一个文件的文字,也可以不用包装起来
{
public:
int LineCount = 0;//总行数
int CharCount = 0;//总字符数
Node *last = nullptr; //最后一个节点,也即最后一行
Node *first = nullptr;//第一个节点,也即第一行
Node *head = nullptr; //头节点,其content为空,next指针指向第一个节点
TextArea() = default; //默认构造函数
};
void PrintNodes(Node *);//打印所有节点(此处为函数声明,定义在后面)
void PrintNode(Node *);//打印单个节点
void DelNode(int, TextArea &);//删除单个节点
Node *GoTo(int, TextArea &); //跳转到特定一行
int main()
{
TextArea text1; //创建一个对象
text1.head = new Node; //创建一个头节点,它的next指向第一个节点,它不存放其他内容
text1.head->LineNum = -1;
char *InFileName = new char[100]; //存放输入文件名
char *OutFileName = new char[100]; //存放输出文件名
cout << "请输入欲输入的文件名(不能包含空格):";
cin >> InFileName;
cout << "请输入欲输出的文件名(不能包含空格):";
cin >> OutFileName;
fstream InFile; //创建fstream文件流对象
InFile.open(InFileName, fstream::in | fstream::out); //打开输入文件
if (!InFile.is_open()) //如果文件成功打开,则InFile.is_open()=1,此处判断是否未成功打开
{
cout << "文件打开失败!是否要新建该文件?Y/N" << endl;
char YN;
cin >> YN;
if (YN >= 'A' && YN <= 'Z')
{
YN += 'a' - 'A'; //大写转小写
}
if (YN == 'y')
{
InFile.open(InFileName, fstream::in | fstream::out | fstream::trunc); //创建文件
}
else
exit(1); //非正常退出
}
Node *CurNode = new Node;
text1.head->next = CurNode;
if (!InFile.eof()) //如果文件没到末尾
{
InFile.getline(CurNode->content, char_num - 1); //先读一行进来,因为文件至少得有一行(即使是空的)【我是这样认为的】
text1.CharCount += strlen(CurNode->content); //字符计数
text1.LineCount++; //行数计数
CurNode->LineNum = text1.LineCount; //存入行号
}
while (!InFile.eof()) //如果文件没到末尾,则一直循环一行一行读取
{
Node *TempNode = CurNode;
CurNode->next = new Node;
CurNode = CurNode->next;
CurNode->priv = TempNode; //创建新节点完毕
text1.LineCount++;
CurNode->LineNum = text1.LineCount;
InFile.getline(CurNode->content, char_num - 1);//读一行内容并存入content(不含回车)
text1.CharCount += strlen(CurNode->content);
//写入新节点完毕
}
InFile.close();//关闭文件
text1.last = CurNode; //存下最后一个节点的指针
text1.first = text1.head->next; //存下第一个节点的指针
char action;
Node *CurNode2 = nullptr; //当前操作的节点,现在还未开始,故为空
Node *temp = nullptr; //供之后可重复使用的temp节点
int CurLine = 0; //当前操作的行号,0行不存在
while (1)
{
cout << "请输入动作(输入H 或 h以显示帮助 ) : ";
cin >> action;
getchar(); //吃掉回车,免得出问题
if (action >= 'A' && action <= 'Z')
{
action += 'a' - 'A'; //大写转小写
}
switch (action)
{
case 'h': //Help
cout << "以下是操作列表(不区分大小写)" << endl;
cout << "V:查看整个文件内容" << endl;
cout << "F:转到第一行" << endl;
cout << "L:转到最后一行" << endl;
cout << "N:转到下一行" << endl;
cout << "P:转到上一行" << endl;
cout << "G:转到特定的某行" << endl;
cout << "D:删除特定某行" << endl;
cout << "O:覆盖当前行内容" << endl;
cout << "S:在当前行查找子串" << endl;
cout << "R:在当前行替换子串" << endl;
cout << "T:显示统计信息" << endl;
cout << "C:保存文件" << endl;
cout << "E:退出编辑器" << endl;
break;
case 'v': //View The Whole File
PrintNodes(text1.head);
break;
case 'g': //Goto A Line
int GoNum;
cout << "请输入要跳转的行数:";
if (!(cin >> GoNum))
{
cout << "输入错误!" << endl;
break;
}
temp = GoTo(GoNum, text1);
if (temp == nullptr)
{
cout << "没有这一行" << endl;
break;
}
CurNode2 = temp;
CurLine = CurNode2->LineNum;
PrintNode(CurNode2);
temp = nullptr; //temp用完后重新置空
break;
case 'e': //Exit
exit(0);
break;
case 'f': //Go To First Line
CurLine = 1;
CurNode2 = GoTo(1, text1);
PrintNode(GoTo(1, text1));
break;
case 'l': //Go To Last Line
CurLine = text1.LineCount;
CurNode2 = GoTo(text1.LineCount, text1);
PrintNode(CurNode2);
break;
case 'n': //Go To Next Line
if (CurLine == 0)//因为行号是从第一行开始,所以第一次要单独判断一下,因为此时CurNode2=nullptr,没办法用
{
CurLine = 1;
CurNode2 = GoTo(1, text1);
PrintNode(GoTo(1, text1));
break;
}
if (CurLine == text1.LineCount)
{
cout << "已是最后一行!" << endl;
break;
}
CurLine++;
CurNode2 = CurNode2->next;
PrintNode(CurNode2);
break;
case 'p': //Go To Previous Line
if (CurLine == 1 || CurLine == 0)
{
cout << "已是第一行!" << endl;
break;
}
CurLine--;
CurNode2 = CurNode2->priv;
PrintNode(CurNode2);
break;
case 'd': //Delete A Line
int DelNum;
cout << "请输入要删除的行号:";
cin >> DelNum;
if (DelNum < CurLine) //要删除的行在当前行之前(如果要删除的在当前行之后,那么无需特别动作,只需执行删除操作即可
{
CurLine--;//当前行号需要减一,但当然行指针不变,依旧指向当前行
}
else if (DelNum == CurLine) //要删除的行跟当前行相同
{
if (CurNode2->next) //如果Curnode2有next,那么就指向它;也即是当前行指针指向被删除行的下一行
{
CurNode2 = CurNode2->next;
}
else //如果CurNode2没有next(也即它是最后一行)
{
if (CurNode2->priv) //有priv
{
CurLine--;
CurNode2 = CurNode2->priv;
}
else //没有next,也没有priv,证明它是第一行
{
//CurLine和CurNode2不变
}
}
}
DelNode(DelNum, text1);//调用删除函数进行进一步操作
break;
case 'i': //Insert A Line After Current Line
if (CurNode2) //CurNode2!=nullptr;也即此刻不是第0行
{
temp = CurNode2;
while (temp->next) //把当前行之后的所有行的行号都加一(小技巧,一般这种形式的while循环,我就读作while(temp有next))
{
temp = temp->next;
temp->LineNum++;
}
temp = new Node;
temp->LineNum = CurLine + 1; //插入的新行的行号为当前行号加一
if (CurNode2->next)
{
temp->priv = CurNode2;
temp->next = CurNode2->next;
CurNode2->next->priv = temp;
CurNode2->next = temp;
}
else //CurNode2无next,即为最后一行,那么新插入的行成为最后一行
{
CurNode2->next = temp;
temp->priv = CurNode2;
temp->next = nullptr;//最后一行没有下一行
}
text1.LineCount++; //总行数加一
cout << "请键入增加行的内容:";
fgets(temp->content, char_num - 1, stdin);
if (temp->content[strlen(temp->content) - 1] == '\n') //fgets会连同空格和回车一起存进content,但我们不要回车,故用此法去掉回车
temp->content[strlen(temp->content) - 1] = 0;
CurLine++;
CurNode2 = temp; //当前行指针指向新插入的行
cout << "插入成功!" << endl;
temp = nullptr;
break;
}
cout << "错误,目前未处于任何一行!" << endl;//进入这行语句,证明CurNode2为空
break;
case 'o': //Override Current Line
if (CurNode2)
{
memset(CurNode2->content, 0, char_num);//清空本行原有内容
cout << "请输入想要将本行替换为的内容:";
fgets(CurNode2->content, char_num - 1, stdin);
if (CurNode2->content[strlen(CurNode2->content) - 1] == '\n')
CurNode2->content[strlen(CurNode2->content) - 1] = 0;
cout << "替换成功!" << endl;
break;
}
cout << "错误,目前未处于任何一行!" << endl;
break;
case 's': //Search For A SubString In Current Line
if (CurNode2)
{
cout << "当前行内容:" << endl;
PrintNode(CurNode2);//先打印一下本行内容
cout << "请输入想要在本行查找的内容:";
char SearchChar[char_num] = {0};//存想要找的子串
fgets(SearchChar, char_num - 1, stdin);//输入想要找的子串
if (SearchChar[strlen(SearchChar) - 1] == '\n')//去除回车
SearchChar[strlen(SearchChar) - 1] = 0;
char *Place = strstr(CurNode2->content, SearchChar); //返回子串第一次出现位置的内存地址
int PlaceInt = Place - CurNode2->content; //转换成下标(数组名就是数组第一个元素的首地址,这样相当于算出来偏移量)
if (PlaceInt < 0)
{
cout << "未找到该子串!" << endl;
break;
}
cout << "子串第一次出现位置为:" << PlaceInt << endl;
break;
}
cout << "错误,目前未处于任何一行!" << endl;
break;
case 'r': //Repalce A SubString In Current Line
if (CurNode2)
{
cout << "当前行内容:" << endl;
PrintNode(CurNode2);
cout << "请输入想要在本行要替换的内容:";
char ToReplaceChar[char_num] = {0};
fgets(ToReplaceChar, char_num - 1, stdin);
if (ToReplaceChar[strlen(ToReplaceChar) - 1] == '\n')
ToReplaceChar[strlen(ToReplaceChar) - 1] = 0;
char *Place = strstr(CurNode2->content, ToReplaceChar);
int PlaceInt = Place - CurNode2->content;//首先先查找要替换的内容
if (PlaceInt < 0)
{
cout << "未找到要替换的内容!" << endl;
break;
}
cout << "请输入,将目标替换成:";
char ReplaceChar[char_num] = {0};
fgets(ReplaceChar, char_num - 1, stdin);
if (ReplaceChar[strlen(ReplaceChar) - 1] == '\n')
ReplaceChar[strlen(ReplaceChar) - 1] = 0;
char FinalChar[char_num] = {0};//存替换完之后整行的内容
int FCCount = 0;//FinalChar的计数器
for (int i = 0; i < strlen(CurNode2->content); i++)//将content的内容一个一个赋给FinalChar
{
if (i == PlaceInt)//当到了要替换的位置
{
for (int j = 0; j < strlen(ReplaceChar); j++)//转而赋值替换后的字符
{
FinalChar[FCCount++] = ReplaceChar[j];
}
i += strlen(ToReplaceChar) - 1;//然后把i的值加上被替换子串的长度(由于是下标,故减一),实现跳过这个子串
continue;
}
else
{
FinalChar[FCCount++] = CurNode2->content[i];
}
}
strcpy(CurNode2->content, FinalChar);//最后把FinalChar复制给content
break;
}
cout << "错误,目前未处于任何一行!" << endl;
break;
case 't': //Show The Whole Line Number And Char Number(拼音"统")
cout << "总行数:" << text1.LineCount << " 行 \t"
<< "总字符数(含空格):" << text1.CharCount << endl;
break;
case 'c': //Save File(拼音"存")
fstream OutFile(OutFileName, fstream::out);
temp = text1.head;
while (temp->next)
{
temp = temp->next;
OutFile << temp->content;//一行一行地写入文件
if (temp->next != nullptr)//如果不是最后一行,那么就在行末加个回车
OutFile << "\n";
}
OutFile.close();
break;
}
}
cout << "==============ALL DONE=================";
}
void PrintNodes(Node *head)//打印所有节点,需要传入一个头节点的指针
{
if (head->next)
{
Node *CurNode = head->next;
cout << CurNode->LineNum << " |";//先打印行号,再打印内容
if (CurNode->content[0])//判断一下内容的第一个字符是不是'\0',即空字符,如果是,则说明此行内容为空,输出一个空行;此处if判断如果不是空字符,则进入if
{
cout << CurNode->content;
}
cout << endl;
while (CurNode->next) //循环打印剩下的所有节点
{
CurNode = CurNode->next;
cout << CurNode->LineNum << " |" << CurNode->content << endl;
}
}
}
void PrintNode(Node *Line)//传入单个节点并打印
{
cout << Line->LineNum << " |";
if (Line->content[0])
{
cout << Line->content;
}
cout << endl;
}
Node *GoTo(int GoNum, TextArea &TA)//跳转到特定的一行,传入要跳转的行号和TextArea的引用,用于调用head等成员;返回要跳转到的行的指针
{
if (TA.head->next)
{
Node *CurNode = TA.head->next;
if (CurNode->LineNum == GoNum)//判断行号是否匹配
{
return CurNode;
}
while (CurNode->next)//遍历判断行号是否匹配
{
CurNode = CurNode->next;
if (CurNode->LineNum == GoNum)
{
return CurNode;
}
}
}
return nullptr; //如果程序执行到此处,则证明以上步骤都没有匹配到对应的行,说明不存在这一行,返回空
//写到这里,我觉得自己是个傻子,为什么不直接先判断一下行号是否小于1或者大于最后一行的行号呢?这样就能直接判断出要跳转的行存不存在了
}
void DelNode(int DelNum, TextArea &TA)//删除特定行
{
if (DelNum < 1 || DelNum > TA.LineCount)//这里学乖了,先判断一下要删除的行是不在范围内
{
cout << "没有这一行!" << endl;
return;
}
Node *CurNode = GoTo(DelNum, TA);//存在则先跳转到要删除的那行
if (CurNode->next)//如果要删除的行有next
{
if (CurNode->priv)//如果它同时还有priv,则证明它处于两行中间
{
CurNode->priv->next = CurNode->next;
CurNode->next->priv = CurNode->priv;//移动它上下两行内部的指针
Node *Temp = CurNode;
while (CurNode->next)//把它后面行的行号都减一
{
CurNode = CurNode->next;
CurNode->LineNum--;
}
delete Temp;
TA.LineCount--;//总行数减一
cout << "删除成功!" << endl;
return;
}
else//没有priv,则说明它是第一行
{
TA.head->next = CurNode->next;//要删除第一行,就得先移动头节点的next
CurNode->next->priv = nullptr;//把它的下一行(它的下一行即将成为第一行)的priv置空
TA.first = CurNode->next;//第一行的指针指向新的第一行
Node *Temp = CurNode;
while (CurNode->next)//行号减一
{
CurNode = CurNode->next;
CurNode->LineNum--;
}
delete Temp;
TA.LineCount--;//总行数减一
cout << "删除成功!" << endl;
return;
}
}
else//要删除的行没有next,证明它是最后一行
{
if (CurNode->priv)//如果它有priv,证明它不是"既是最后一行又是第一行"
{
CurNode->priv->next = nullptr;//只需要把它的前一行的next置空
delete CurNode;
TA.LineCount--;//总行数减一
cout << "删除成功!" << endl;
return;
}
else//此处既没有next也没有priv,那它就是唯一的一行,所以不能删掉
{
memset(CurNode->content, 0, char_num);//只需清空内容即可
cout << "删除成功!" << endl;
return;
}
}
}
]]>