JianGuanTHU / UOJ_Offline

Offline test system for THUOOP
7 stars 0 forks source link

Proposal(第五次作业,文本编辑器) #33

Open nine-point-eight-p opened 2 years ago

nine-point-eight-p commented 2 years ago

题目描述

有了STL的帮助,我们可以做出很多东西。这次让我们来做一个简单的文本编辑器TextEditor

重要说明它的行为和一般的文本编辑器基本一致,如windows自带的记事本、vscode等。如果有理解上的困难,可以在这些编辑器上操作一下,来观察其功能,例如光标的移动规律,撤销、重做的模式等等。

TextEditor的基本构成有:

具体来说,TextEditor具有以下功能:

main.cpp将会给定。请编写TextEditor.h,可以增加文件。将你所写的文件和makefile打包提交。

备注

hzhwcmhf commented 2 years ago

基本可行。撤销和重做需要再仔细定义一下,比如复制不算操作、剪切和粘贴算操作? 内容已经足够了,不需要添加别的内容 麻烦提供一下main.cpp的代码,样例输入输出,部分分规则

nine-point-eight-p commented 2 years ago

main.cpp:

#include "editor.h"
#include <string>
#include <iostream>

using std::string;
using std::cin;
using std::cout;
using std::endl;

int main()
{
    TextEditor t;
    string opr;
    while (!cin.eof())
    {
        cin >> opr;
        if (opr == "MOVE")
        {
            int row, col;
            cin >> row >> col;
            t.moveCursor({row, col});
        }
        else if (opr == "SELECT")
        {
            int row1, col1, row2, col2;
            cin >> row1 >> col1 >> row2 >> col2;
            t.select({row1, col1}, {row2, col2});
        }
        else if (opr == "WRITE")
        {
            cin.get(); // Eliminate the space
            cin >> t;
        }
        else if (opr == "ENTER")
        {
            t.write("\n");
        }
        else if (opr == "PRINT")
        {
            cout << t;
        }
        else if (opr == "DELETE")
        {
            t.del();
        }
        else if (opr == "COPY")
        {
            t.copy();
        }
        else if (opr == "CUT")
        {
            t.cut();
        }
        else if (opr == "PASTE")
        {
            t.paste();
        }
        else if (opr == "UNDO")
        {
            t.undo();
        }
        else if (opr == "REDO")
        {
            t.redo();
        }
        else if (opr == "CLEARH")
        {
            t.clearHistory();
        }
        else if (opr == "SCREEN")
        {
            t.screenShot();
        }
        else if (opr == "QUIT")
        {
            break;
        }
        else
            cout << "Invalid operation" << endl;
        // t.screenShot();
    }
    return 0;
}

缺点就是if-else太多了……

输入输出的话可以从main.cpp看出来。输入大概就像

WRITE abcde
SELECT 0 0 0 2
DELETE
UNDO
PRINT
SCREEN

输出就是对应行(不带光标)的内容或整个文本(带光标)的内容。说实话,我感觉输入(>>)设计得有点奇怪。

关于撤销和重做:撤销和重做实际上只涉及出现写入/删除的过程。所以剪切和粘贴会被考虑到撤回的操作中,而复制不会。不过剪贴板里的东西不会因为撤销/重做而发生改变。

关于部分分,目前考虑:基础的功能是输入输出、光标操作、单行内的写入删除;然后增加多行范围的写入删除,以及复制、剪切、粘贴;最后是撤销、重做等。

hzhwcmhf commented 2 years ago

我觉得可以适当减少操作。比如剪切包含复制,我觉得只留一个就行。输出所在行和screen差不多,可以考虑不要。clearHistory可能也没太大必要。 输入确实有点奇怪,而且提醒一下cin.get()在win下不一定能把换行吃掉,win换行符有两个

nine-point-eight-p commented 2 years ago

好的,那几个都删掉了。我觉得重载输入输出其实也没什么意思,反而更混乱了,干脆也不要了。输入只靠write,输出只靠screen。所以write我给改成了:

        else if (opr == "WRITE")
        {
            std::string data;
            cin.get(); // Clear the space
            getline(cin, data);
            t.write(data);
        }

那个get我是想吃掉一个空格(?)因为输入的时候是这样的:

WRITE hello world

然后就把剩下的内容都算作要写入的东西。不过好像很难换行,所以我才设置了一个ENTER选项。

hzhwcmhf commented 2 years ago

好的,麻烦再给下修改后的完整题面和main吧 另外能给TextEditor的实现给些限制(或者提示)吗,比如说剪贴板和命令之类的对象?

nine-point-eight-p commented 2 years ago

题目描述

有了STL的帮助,我们可以做出很多东西。这次让我们来做一个简单的文本编辑器TextEditor

重要说明它的行为和一般的文本编辑器基本一致,如windows自带的记事本、vscode等。如果有理解上的困难,可以在这些编辑器上操作一下,来观察其功能,例如光标的移动规律,撤销、重做的模式等等。

TextEditor的基本构成有:

具体来说,TextEditor具有以下功能:

main.cpp已给定,如下:

#include "editor.h"
#include <string>
#include <iostream>

using std::string;
using std::cin;
using std::cout;
using std::endl;

int main()
{
    TextEditor t;
    string opr;
    while (!cin.eof())
    {
        cin >> opr;
        if (opr == "MOVE")
        {
            int row, col;
            cin >> row >> col;
            t.moveCursor({row, col});
        }
        else if (opr == "SELECT")
        {
            int row1, col1, row2, col2;
            cin >> row1 >> col1 >> row2 >> col2;
            t.select({row1, col1}, {row2, col2});
        }
        else if (opr == "WRITE")
        {
            std::string data;
            cin.get(); // Clear the space between "WRITE" and data
            getline(cin, data);
            t.write(data);
        }
        else if (opr == "ENTER")
        {
            t.write("\n");
        }
        else if (opr == "DELETE")
        {
            t.del();
        }
        else if (opr == "COPY")
        {
            t.copy();
        }
        else if (opr == "PASTE")
        {
            t.paste();
        }
        else if (opr == "UNDO")
        {
            t.undo();
        }
        else if (opr == "REDO")
        {
            t.redo();
        }
        else if (opr == "SCREEN")
        {
            t.screenShot();
        }
        else if (opr == "QUIT")
        {
            break;
        }
        else
            cout << "Invalid operation" << endl;
    }
    return 0;
}

请编写editor.h,可以增加文件。将你所写的文件和makefile打包提交。

hzhwcmhf commented 2 years ago

下发文件给大家参考吧,不用限制死

nine-point-eight-p commented 2 years ago

比如这样?

// editor.h
#pragma once

#include "command.h"
#include <vector>
#include <list>

class TextEditor {
private:
    using Position = std::pair<int, int>;
    std::vector<std::string> data;
    std::list<Command*> cmd;
    // To be filled or changed...
public:
    // To be filled or changed...
};
// command.h
#pragma once

class Command {
private:
    using Position = std::pair<int, int>;
    Position beg, end;
    // To be filled or changed...
public:
    // To be filled or changed...
};
hzhwcmhf commented 2 years ago

TextEditor里应该还有块剪贴板? Command里可以怎么记operation?是打算写operation和参数?这里是否有可能用继承实现不同Command,undo(TextEditor&)做成虚函数? 另外cmd的list里好像没有必要用指针,直接对象可能更方便一些吧

nine-point-eight-p commented 2 years ago

TextEditor里应该还有块剪贴板?

是的,的确有剪贴板。

Command里可以怎么记operation?是打算写operation和参数?这里是否有可能用继承实现不同Command,undo(TextEditor&)做成虚函数?

目前我的方法确实是通过继承实现不同的command,有execute、unexecute两个虚函数来记录行为。接口就是(TextEditor&)。如果不用指针的话,好像就没办法用虚函数了(?)

hzhwcmhf commented 2 years ago

啊对,需要指针,那么Command虚函数接口也给一下吧。这部分的代码解释需要再详细一点

nine-point-eight-p commented 2 years ago

目前我是这么写的:

class Command {
public:
    using Position = std::pair<int, int>;
    using Data = std::string;
    using DataSet = std::vector<Data>;
protected:
    Position beg, end;
    Command(Position beg, Position end);
public:
    virtual ~Command() = default;

    virtual void execute(TextEditor& editor) = 0;
    virtual void unexecute(TextEditor& editor) = 0;
};

不过还有一些小问题,比如我还没太想好怎么处理“连续退回”这种事情;再比如昨晚发现,如果选中一块有文字的区域并写入,效果上等价于先删除再写入,但是撤销时将撤回至未删除时的情形,且处于选中状态。感觉这一块可能比较复杂,我在考虑取消“连续退回”这种东西,避免过于复杂。

hzhwcmhf commented 2 years ago

这个你得对每个操作有个详细的说明,可以适当简化,不能让大家自己去看文本编辑器 总之你再考虑一下,题面细化以后我们再讨论

nine-point-eight-p commented 2 years ago

题目描述

有了STL的帮助,我们可以做出很多东西。这次让我们来做一个简单的文本编辑器TextEditor

说明:它的行为和一般的文本编辑器基本一致,如windows自带的记事本、vscode等。如有困惑,可在这些编辑器上尝试以作参考,但一切以题目描述为准(因为有简化和修改)。

组成

TextEditor的基本构成有:

功能

具体来说,TextEditor具有以下功能:

任务

main.cpp已给定,如下:

#include "editor.h"
#include <string>
#include <iostream>

using std::string;
using std::cin;
using std::cout;
using std::endl;

int main()
{
    TextEditor t;
    string opr;
    while (!cin.eof())
    {
        cin >> opr;
        if (opr == "MOVE")
        {
            int row, col;
            cin >> row >> col;
            t.moveCursor({row, col});
        }
        else if (opr == "SELECT")
        {
            int row1, col1, row2, col2;
            cin >> row1 >> col1 >> row2 >> col2;
            t.select({row1, col1}, {row2, col2});
        }
        else if (opr == "WRITE")
        {
            std::string data;
            cin.get(); // Clear the space between "WRITE" and data
            getline(cin, data);
            t.write(data);
        }
        else if (opr == "ENTER")
        {
            t.enter();
        }
        else if (opr == "DELETE")
        {
            t.del();
        }
        else if (opr == "COPY")
        {
            t.copy();
        }
        else if (opr == "PASTE")
        {
            t.paste();
        }
        else if (opr == "UNDO")
        {
            t.undo();
        }
        else if (opr == "REDO")
        {
            t.redo();
        }
        else if (opr == "SCREEN")
        {
            t.screenShot();
        }
        else if (opr == "QUIT")
        {
            break;
        }
        else
            cout << "Invalid operation" << endl;
    }
    return 0;
}

这里还提供一些已有代码供你参考,但你可以部分使用或不使用。

// editor.h
#pragma once

#include "command.h"
#include <vector>
#include <list>

// Do what you want to all the files...

class TextEditor {
private:
    using Position = std::pair<int, int>;
    std::vector<std::string> data; // 数据区
    std::vector<std::string> clipboard; // 剪贴板
    std::list<Command*> cmd; // 操作序列
    // To be filled or changed...
public:
    // To be filled or changed...
};
// command.h
#pragma once

// Do what you want to all the files...

// 操作基类
class Command {
public:
    using Position = std::pair<int, int>;

    Command();
    virtual ~Command() = default;

    virtual void execute(TextEditor& editor) = 0;
    virtual void unexecute(TextEditor& editor) = 0;
};

// 写入操作
class WriteCommand : public Command {
private:
    // Add some data?
public:
    // Write a constructor?
    void execute(TextEditor& editor) override;
    void unexecute(TextEditor& editor) override;
};

请进一步编写editor.h,也可以增加文件。将除main.cpp以外的文件和makefile打包提交。

hzhwcmhf commented 2 years ago

我觉得为啥不把现有的所有操作都当成单元操作,只要恢复到操作前状态即可?至于写入时有删除我觉得也没啥,可以认为所有写入都带删除,没有选中的话删除为空就是了

nine-point-eight-p commented 2 years ago

那也行……我现在觉得,记录操作的方法好像相当麻烦,也许不如直接保存状态(当然不需要保存全部的文本,保存一部分就行了),这样没准更容易恢复状态。

hzhwcmhf commented 2 years ago

至于怎么保存可以让同学自己决定,接口目前这样应该没问题吧?我感觉execute和unexecute的提示还是不太清楚,这个能加点说明或者示例吗。

另外就是把修改后的题面再发一下吧。功能里面输入输出和编辑应该可以合并,输入说了两遍没有必要。

nine-point-eight-p commented 2 years ago

题目描述

有了STL的帮助,我们可以做出很多东西。这次让我们来做一个简单的文本编辑器TextEditor

说明:它的行为和一般的文本编辑器基本一致,如windows自带的记事本、vscode等。如有困惑,可在这些编辑器上尝试以作参考,但一切请以题目描述为准(因为有简化和修改)。

组成

TextEditor的基本构成有:

功能

具体来说,TextEditor具有以下功能:

任务

main.cpp已给定,如下:

#include "editor.h"
#include <string>
#include <iostream>

using std::string;
using std::cin;
using std::cout;
using std::endl;

int main()
{
    TextEditor t;
    string opr;
    while (!cin.eof())
    {
        cin >> opr;
        if (opr == "MOVE")
        {
            int row, col;
            cin >> row >> col;
            t.moveCursor({row, col});
        }
        else if (opr == "SELECT")
        {
            int row1, col1, row2, col2;
            cin >> row1 >> col1 >> row2 >> col2;
            t.select({row1, col1}, {row2, col2});
        }
        else if (opr == "WRITE")
        {
            std::string data;
            cin.get(); // Clear the space between "WRITE" and data
            getline(cin, data);
            t.write(data);
        }
        else if (opr == "ENTER")
        {
            t.enter();
        }
        else if (opr == "DELETE")
        {
            t.del();
        }
        else if (opr == "COPY")
        {
            t.copy();
        }
        else if (opr == "PASTE")
        {
            t.paste();
        }
        else if (opr == "UNDO")
        {
            t.undo();
        }
        else if (opr == "REDO")
        {
            t.redo();
        }
        else if (opr == "SCREEN")
        {
            t.screenShot();
        }
        else if (opr == "QUIT")
        {
            break;
        }
        else
            cout << "Invalid operation" << endl;
    }
    return 0;
}

这里还提供一些已有代码供你参考,但你可以部分使用或不使用。这些文件里还有一些帮助性的说明。

// editor.h
#pragma once

#include "command.h"
#include <vector>
#include <list>

// Do what you want to all the files...

class TextEditor {
private:
    using Position = std::pair<int, int>;
    std::vector<std::string> data; // 数据区
    std::vector<std::string> clipboard; // 剪贴板
    std::list<Command*> cmd; // 操作序列,可参考command.h的说明
    // To be filled or changed...
public:
    // To be filled or changed...
};
// command.h
#pragma once

// Do what you want to all the files...

// 关于保存历史记录的提示:
// 可以通过保存操作的步骤来记录。首先从Command基类为需要的操作派生出具体的子类。
// 传入TextEditor的引用,调用unexecute函数可以对TextEditor“反向”操作,从而达到撤销后的状态;
// 调用execute函数则可以对TextEditor进行“正向”操作,从而达到重做后的状态。
// 即通过这两个函数修改TextEditor,进实现状态的转移。
// 由此,可以使用list<Command*>来储存并管理各个步骤,进而实现撤销/重做等等。
// 
// 当然,也可以通过保存每次操作后文本的状态来记录。
// 此外,如有需要,可以参考memento设计模式或command设计模式。

// 操作基类
class Command {
public:
    using Position = std::pair<int, int>;

    Command();
    virtual ~Command() = default;

    virtual void execute(TextEditor& editor) = 0;
    virtual void unexecute(TextEditor& editor) = 0;
};

// 写入操作
class WriteCommand : public Command {
private:
    // Add some data?
public:
    // Write a constructor according to the data
    void execute(TextEditor& editor) override;
    void unexecute(TextEditor& editor) override;
};

请进一步编写editor.h,也可以增加文件。将除main.cpp以外的文件和makefile打包提交。

hzhwcmhf commented 2 years ago

题目是不是还没样例输入输出,需要补充一个。注意保证一个测试点是样例,并且说明部分分的设置

请将完整题面、数据(包括数据生成文件)、judger和答案程序,自行测试通过。完成之后发到邮箱 huangfei382@163.com。对于编写judger有问题可在小教员群(或找助教)讨论。