Skip to content

ggsaddy/lab

Repository files navigation

lab

fdu高级软件开发技术小组作业

Lab1:基于字符命令界面的文本编辑器

实验目标

本实验要求实现一个基于命令行的文本编辑器,支持同时打开多个文本文件,提供工作区管理、日志记录、状态持久化等功能。

实验重点考察:

  • 面向对象建模能力
  • 模块化设计与依赖管理
  • 设计模式的合理应用
  • 自动化测试能力

一、功能需求

1. 工作区模块 (Workspace)

核心职责:

  • 管理当前会话的全局状态,包括已打开文件列表、当前活动文件、文件修改状态、日志开关等
  • 协调命令与具体编辑器之间的交互,例如 load / save / close 等操作
  • 状态持久化:程序退出后能保存工作区状态,下次启动时恢复
  • 发布事件供日志记录等模块订阅

设计要点:

  • 工作区中可以有多个编辑中的文件(Editor),有一个当前的活动文件(Active Editor)
  • 每个Editor有独立的undo/redo状态
  • 持久化保存的内容
    • 打开的文件列表
    • 当前活动文件
    • 文件修改状态(modified标记)
    • 日志开关状态
  • 不需要持久化的内容:undo/redo历史记录

建议使用的设计模式:

  • 备忘录模式 (Memento):用于工作区状态的持久化和恢复
  • 观察者模式 (Observer):用于事件通知机制

2. 编辑器模块 (Editor)

Lab1仅支持文本编辑器即可,在Lab2中将增加XML编辑器。

文本编辑器 (TextEditor)

功能职责:

  • 支持基本文本编辑操作:追加(append)、插入(insert)、删除(delete)、替换(replace)
  • 支持显示操作:指定行号范围显示文本内容(show)
  • 所有编辑操作后自动标记文件为已修改
  • 提供必要的错误反馈(如范围越界)

数据结构要求:

  • 使用行数组(List<String>)存储文本,每个元素是一行
  • 保存时用换行符连接各行
  • 这样可以方便地通过行号定位和操作

建议使用的设计模式:

  • 命令模式 (Command):实现undo/redo功能
  • 装饰器模式 (Decorator)

文本文件示例:

The quick brown fox
jumps over the lazy dog.
This line contains     extra spaces.

3. 日志模块 (Logging)

核心功能:记录每一次命令执行,包括执行的时间戳,并持久化到日志文件中。

功能职责:

  • 若文件第一行是 # log,则打开该文件时自动启用日志记录
  • 记录每一条命令的执行内容与时间戳
  • 每次程序启动视为一次新的会话(Session),日志以时间段划分
  • 支持日志开关,可通过命令手动启用/关闭(log-on / log-off)
  • 支持查看日志记录(log-show)
  • 日志写入 .filename.log 文件,永久保存
  • 若日志记录失败仅提示警告,不中断程序正常运行

建议使用的设计模式:

  • 观察者模式 (Observer):日志模块作为观察者监听命令执行事件

日志文件格式:

session start at 20251024 09:41:33
20251024 09:41:40 load lab.txt
20251024 09:42:05 append "test line"
20251024 09:44:27 save
20251024 09:44:50 close

格式说明:

  • 每条命令一行,格式为:时间戳 命令参数
  • 会话开始用 session start at 标识
  • 命令参数与用户交互时保持一致

示例场景:

文件 lab.txt 内容:

# log
今天是个写代码的好日子
记得把实验报告补完

打开该文件后,自动在 .lab.txt.log 中记录操作。

二、命令设计

命令约定

  • 命令默认对当前活动文件生效
  • <file> 表示文件路径
  • line:col 表示行号和列号(从1开始计数)
  • 带空格的文本参数使用双引号包裹
  • 所有命令参数区分大小写
  • 文件编码格式:统一使用UTF-8编码

命令速查表

工作区命令

命令 功能 必需参数 可选参数
load <file> 加载文件 文件路径 -
save [file|all] 保存文件 - file/all
init <file> [with-log] 创建新缓冲区 文件 with-log
close [file] 关闭文件 - file
edit <file> 切换活动文件 文件 -
editor-list 显示文件列表 - -
dir-tree [path] 显示目录树 - path
undo 撤销 - -
redo 重做 - -
exit 退出程序 - -

文本编辑命令

命令 功能 适用文件
append "text" 追加文本 .txt
insert <line:col> "text" 插入文本 .txt
delete <line:col> <len> 删除字符 .txt
replace <line:col> <len> "text" 替换文本 .txt
show [start:end] 显示内容 .txt

日志命令

命令 功能
log-on [file] 启用日志
log-off [file] 关闭日志
log-show [file] 显示日志

2.1 工作区命令

1. load - 加载文件

load <file>

功能:加载文件。

行为:

  • 文件已存在:读取并解析内容
  • 文件不存在:创建新文件,标记为已修改
  • 文件成为当前活动文件

2. save - 保存文件

save [file|all]

功能:保存文件内容到磁盘。

参数说明:

  • 不指定参数:保存当前活动文件
  • file:保存指定文件
  • all:保存所有已打开的文件

行为:

  • 保存成功后清除已修改标记
  • 若路径无法写入,提示错误信息

3. init - 创建新缓冲区

init <file> [with-log]

功能:创建一个未保存的新缓冲文件,并初始化基础结构。

参数说明:

  • file:创建纯文本文件
  • with-log(可选):是否在第一行添加 # log 以启用日志

初始化内容:

创建文本文件(init test.text with-log):

# log

说明:

  • 新缓冲区标记为已修改,需要使用 save 命令指定路径保存
  • 创建后自动成为当前活动文件

4. close - 关闭文件

close [file]

功能:关闭当前活动文件或指定文件。

行为:

  • 文件已修改且未保存:提示"文件已修改,是否保存? (y/n)"
  • 关闭后,如果还有其他打开的文件,切换到最近使用的文件

5. edit - 切换活动文件

edit <file>

功能:切换当前活动文件。

行为:

  • 文件必须已在工作区中打开
  • 切换失败提示:"文件未打开: [file]"

6. editor-list - 显示文件列表

editor-list

功能:显示工作区中所有打开的文件及其状态。

显示格式(可选以下任一种):

格式1:

* file1.txt [modified]
  file2.txt

格式2:

> file1.txt*
  file2.txt

说明:

  • 当前活动文件标记* (格式1)或 > (格式2)
  • 已修改未保存标记[modified] (格式1)或后缀 * (格式2)

7. dir-tree - 显示工作目录文件树

dir-tree [path]

功能:以树形结构显示当前工作目录(或指定目录)的文件和文件夹。

参数说明:

  • 不指定参数:显示当前工作目录
  • path:指定要显示的目录路径

显示格式:

├── visitor
│   ├── visitor.worksheet.sc
│   ├── visitor.scala
│   ├── README.md
│   ├── diagram.md
│   └── livedemo
│       ├── visitor.scala
│       └── livedemo.worksheet.sc
└── strategy
    ├── README.md
    ├── livedemo
    │   ├── strategy.scala
    │   └── strategy.worksheet.sc
    ├── strategy.scala
    └── strategy.worksheet.sc

说明:

  • 使用 ├──└── 字符绘制树形结构
  • 显示目录和文件的层级关系

8. undo - 撤销

undo

功能:撤销上一次编辑操作。

说明:

  • 只撤销会改变文件状态的命令
  • 显示类命令(show、dir-tree、editor-list等)不进入撤销栈

9. redo - 重做

redo

功能:重做上一次撤销的操作。


10. exit - 退出程序

exit

功能:退出编辑器程序。

行为:

  • 保存工作区状态到配置文件
  • 若有未保存的文件,逐一提示是否保存

2.2 文本编辑命令

1. append - 追加文本

append "text"

功能:在文件末尾追加一行文本。

示例:

原文:
Hello world

执行: append "New line"

结果:
Hello world
New line

2. insert - 插入文本

insert <line:col> "text"

功能:在指定位置插入文本。

参数说明:

  • line:行号(从1开始)
  • col:列号(从1开始,表示插入到第几个字符前)
  • "text":要插入的文本内容

行为说明:

  • 插入位置在现有字符之前
  • 文本中可以包含换行符(\n),将自动拆分为多行

异常处理:

  • 行号或列号越界:提示"行号或列号越界"
  • 空文件插入非1:1位置:提示"空文件只能在1:1位置插入"

示例:

原文:
abcdef

执行: insert 1:4 "XYZ"

结果:
abcXYZdef

3. delete - 删除字符

delete <line:col> <len>

功能:从 line:col 位置开始,删除连续 len 个字符。

行为说明:

  • 删除范围不可跨行
  • 删除长度不可超过该行剩余字符数

异常处理:

  • 删除长度超出该行剩余字符:提示"删除长度超出行尾"
  • 行号或列号越界:提示相应的范围错误

示例:

原文:
Hello world

执行: delete 1:7 5

结果:
Hello 

4. replace - 替换字符

replace <line:col> <len> "text"

功能:删除从指定位置起 len 个字符,并插入 "text"

行为说明:

  • 等效于先执行 delete,再执行 insert
  • 替换文本可为空字符串,效果等同于删除

示例:

原文:
fast fox

执行: replace 1:1 4 "slow"

结果:
slow fox

5. show - 显示文本内容

show [startLine:endLine]

功能:显示文本编辑器中指定范围的内容(按行)。

参数说明:

  • 不指定参数:显示全文
  • startLine:起始行号(从1开始)
  • endLine:结束行号(包含)

适用对象:仅适用于文本编辑器(.txt 文件)

示例:

> show
1: Hello world
2: This is line 2
3: This is line 3

> show 1:2
1: Hello world
2: This is line 2

说明:显示类命令不改变文件状态,不进入撤销栈

2.3 日志命令

1. log-on - 启用日志

log-on [file]

功能:为指定文件(或当前活动文件)启用日志记录。

参数说明:

  • 不指定参数:为当前活动文件启用日志
  • file:为指定文件启用日志

行为说明:

  • 后续该文件的所有编辑操作、保存行为将被记录到 .filename.log 文件中
  • 如果文件首行已是 # log,可自动启用

2. log-off - 关闭日志

log-off [file]

功能:关闭对指定文件(或当前活动文件)的日志记录。

参数说明:

  • 不指定参数:关闭当前活动文件的日志
  • file:关闭指定文件的日志

行为说明:

  • 停止监听该文件的日志事件,但不删除已有日志

3. log-show - 显示日志

log-show [file]

功能:显示指定文件(或当前活动文件)的日志记录。

参数说明:

  • 不指定参数:显示当前活动文件的日志
  • file:显示指定文件的日志

输出格式:显示 .filename.log 文件的内容。

Lab2:基于字符命令界面的多文本编辑器

实验目标

本实验在Lab1的基础上要求实现一个基于命令行的多文件编辑器,包含纯文本编辑器XML编辑器两类,增加编辑时长统计、拼写检查两大模块。

一、功能需求

1. 编辑器模块

XML编辑器 (XmlEditor)

XML说明: XML (eXtensible Markup Language) 是一种用于存储和传输数据的标记语言,具有良好的可读性和可扩展性,广泛应用于配置文件、数据交换等场景。

功能职责:

  • 支持元素级编辑操作:插入元素(insert-before)、追加子元素(append-child)、修改元素ID(edit-id)、修改元素文本(edit-text)、删除元素(delete)
  • 支持树形结构的可视化输出(xml-tree)
  • 支持拼写检查功能:扫描文档文本节点并输出拼写错误报告

数据结构要求:

  • 解析XML文件为树形结构(DOM树)
  • 内部维护元素节点,并建立 id -> element 的映射以支持快速查找
  • 保存时序列化回XML格式

建议使用的设计模式:

  • 命令模式 (Command):实现undo/redo功能
  • 组合模式 (Composite):表示XML树形结构
  • 装饰器模式 (Decorator):自动标记文件修改状态

XML文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore id="root">
    <book id="book1" category="COOKING">
        <title id="title1" lang="en">Everyday Italian</title>
        <author id="author1">Giada De Laurentiis</author>
        <year id="year1">2005</year>
        <price id="price1">30.00</price>
    </book>
    <book id="book2" category="CHILDREN">
        <title id="title2" lang="en">Harry Potter</title>
        <author id="author2">J K. Rowling</author>
    </book>
</bookstore>

XML语法规则:

  1. <?xml version="1.0" encoding="UTF-8" ?> 必须写在首行(除非第一行是 # log 注释)
  2. 根标签只能有一个,子标签可以有多个,且必须成对存在
  3. 标签属性值必须用双引号包起来
  4. 每个元素必须有唯一的 id 属性,用于命令操作中的元素定位
  5. 本次实验不考虑注释、自闭合标签等高级特性

重要说明:

  • 要求所有元素都有id是为了简化实现。在实际应用中XML元素不一定都有id,但在本实验中统一要求所有元素必须有唯一id

  • 元素定位:所有XML编辑命令中的元素ID参数都是指元素的 id 属性值,可以通过id精确定位任何元素

2. 统计模块 (Statistics)

核心功能:记录每个文件在当前会话(Session)中的编辑时长,并以可读格式显示。

会话(Session)定义:

  • 会话:从程序启动到退出的一次完整运行周期
  • 每次启动程序开始新的会话,所有文件的编辑时长重置为0
  • 工作区状态恢复不会恢复编辑时长

时长计算规则:

  • 开始计时:当文件成为活动文件时(通过 loadedit 命令)
  • 停止计时:当切换到其他文件、关闭文件或退出程序时
  • 累计时长:一个会话中,文件每次成为活动文件都会累计时长
  • 重置时长:文件关闭后,如果再次打开(load),时长重置为0

显示要求:

  • editor-list 命令中,每个文件名后显示编辑时长
  • 使用可读格式:根据时长大小自动选择合适单位

时长格式规范:

时长范围 显示格式 示例
< 1分钟 X秒 45秒
1-59分钟 X分钟 25分钟
1-23小时 X小时Y分钟 2小时15分钟
≥ 24小时 X天Y小时 1天3小时

建议使用的设计模式:

  • 装饰器模式 (Decorator):在显示文件列表时,为每个文件名添加时长信息
  • 观察者模式 (Observer):监听文件切换事件,自动更新时长统计

说明:

  • 统计模块是相对独立的横切功能,不应与核心编辑功能强耦合
  • 如果统计功能失败,应仅提示警告,不影响其他功能继续执行

3. 拼写检查模块 (Spell Checking)

核心考察点:主要考察架构设计能力第三方库管理能力,而非算法实现。

选择合适的拼写检查服务,实现对编辑器中的文本内容进行拼写检查,并报告错误。

可以考虑以下拼写检查服务:

API:https://dev.languagetool.org/public-http-api

Java:https://dev.languagetool.org/java-api

Python:spell-checker库或pyspellchecker

考察重点:

  1. 依赖隔离:第三方库依赖被限制在适配器内
  2. 接口抽象:定义清晰接口,编辑器依赖接口而非实现
  3. 依赖注入:依赖从外部传入,而非内部创建
  4. 可测试性:使用Mock对象测试,无需真实库

建议使用的设计模式: 适配器模式 (Adapter)

4. 日志增强

日志模块在不改变核心行为的基础上,新增“按文件首行配置的日志过滤”能力。

  • 核心功能:通过文件首行的 # log 行为该文件启用日志,并可追加参数过滤不需要记录的命令。
  • 语法规则:
    • # log:启用该文件的日志记录(保持原有行为)。
    • # log -e <cmd> [-e <cmd> ...]:排除指定命令的日志记录,-e 可重复出现以排除多个命令。
  • 示例:
    • 首行写入 # log -e append -e delete 表示不记录该文件的 appenddelete 命令日志。
  • 行为说明:
    • 过滤仅作用于该文件的日志记录,适用于文本编辑命令与XML编辑命令(例如 insert-beforeappend-child 等)。
    • 未识别或不存在的命令名将被忽略,并在日志模块内以告警方式提示;不影响程序正常运行。
    • 日志写入失败仅提示警告,不阻断编辑流程(与既有日志策略一致)。

二、 命令设计

命令速查表

在Lab2中,相比Lab1的命令集有以下新增和变动

工作区命令

命令 功能 必需参数 可选参数
init <text|xml> [with-log] 创建新缓冲区,增加xml文件 文件类型 with-log
editor-list 显示文件列表(支持时长显示) - -

XML编辑命令

命令 功能 适用文件
insert-before <tag> <newId> <targetId> ["text"] 插入元素 .xml
append-child <tag> <newId> <parentId> ["text"] 追加子元素 .xml
edit-id <oldId> <newId> 修改元素ID .xml
edit-text <elementId> ["text"] 修改元素文本 .xml
delete <elementId> 删除元素 .xml
xml-tree [file] 显示XML树 .xml

拼写检查命令

命令 功能 适用文件
spell-check [file] 拼写检查 .txt .xml

2.1 工作区命令

1. init - 创建新缓冲区

init <text|xml> [with-log]

功能:创建一个未保存的新缓冲文件,并初始化基础结构。

参数说明:

  • text:创建纯文本文件
  • xml:创建XML文件,写入合法的空结构
  • with-log(可选):是否在第一行添加 # log 以启用日志

初始化内容:

创建文本文件(init text with-log):

# log

创建XML文件(init xml):

<?xml version="1.0" encoding="UTF-8"?>
<root id="root">
</root>

说明:

  • 新缓冲区标记为已修改,需要使用 save 命令指定路径保存
  • 创建后自动成为当前活动文件

2. editor-list - 显示文件列表

editor-list

功能:显示工作区中所有打开的文件及其状态。

显示格式(可选以下任一种):

格式1:

* file1.txt [modified] (2小时15分钟)
  file2.xml (45秒)

格式2:

> file1.txt* (2小时15分钟)
  file2.xml (45秒)

说明:

  • 当前活动文件标记* (格式1)或 > (格式2)

  • 已修改未保存标记[modified] (格式1)或后缀 * (格式2)

  • 编辑时长:括号内显示当前会话中的编辑时长

2.2 XML编辑命令

1. insert-before - 插入元素

insert-before <tagName> <newId> <targetId> ["text"]

功能:在目标元素前(同级)插入一个新元素。

参数说明:

  • tagName:新插入元素的标签名
  • newId:新元素的唯一ID,不可与已有元素重复
  • targetId:目标元素的ID,新元素将被插入到该元素前
  • "text":可选,新元素的文本内容

异常处理:

  • newId 已存在:提示"元素ID已存在: [newId]"
  • targetId 不存在:提示"目标元素不存在: [targetId]"
  • 尝试在根元素前插入:提示"不能在根元素前插入元素"

示例:

insert-before book newBook book1 ""

2. append-child - 追加子元素

append-child <tagName> <newId> <parentId> ["text"]

功能:在某元素内追加一个子元素(作为最后一个子元素)。

参数说明:

  • tagName:要追加的子元素标签名
  • newId:子元素ID,需唯一
  • parentId:父元素ID
  • "text":可选,子元素的文本内容

异常处理:

  • parentId 无效:提示"父元素不存在: [parentId]"
  • newId 重复:提示"元素ID已存在: [newId]"

示例:

append-child price price4 book1 "29.99"

3. edit-id - 修改元素ID

edit-id <oldId> <newId>

功能:修改某个元素的ID。

参数说明:

  • oldId:原始ID,必须存在
  • newId:目标ID,必须未被占用

异常处理:

  • oldId 不存在:提示"元素不存在: [oldId]"
  • newId 已被占用:提示"目标ID已存在: [newId]"
  • 尝试修改根元素ID:提示"不建议修改根元素ID"

示例:

edit-id book1 book001

4. edit-text - 修改元素文本

edit-text <elementId> ["text"]

功能:修改某元素的文本内容。

参数说明:

  • elementId:元素的ID
  • "text":新文本内容(可选),若为空字符串或省略则清空原内容

异常处理:

  • elementId 不存在:提示"元素不存在: [elementId]"

示例:

edit-text title1 "New Book Title"

5. delete - 删除元素

delete <elementId>

功能:删除指定ID的元素(包括其所有子元素)。

异常处理:

  • elementId 不存在:提示"元素不存在: [elementId]"
  • 尝试删除根元素:提示"不能删除根元素"

示例:

delete book1

6. xml-tree - 显示XML树形结构

xml-tree [file]

功能:以树形结构打印XML文件内容,展示元素的层级关系、属性和文本内容。

参数说明:

  • 不指定参数:显示当前活动文件
  • file:显示指定XML文件

适用对象:仅适用于XML编辑器(.xml 文件)

输出格式要求:

  • 使用树形字符(├──└──)或缩进表示层级关系
  • 显示元素的所有属性(包括id)
  • 显示元素的文本内容(如果有)

示例:

原XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore id="root">
    <book id="book1" category="COOKING">
        <title id="title1" lang="en">Everyday Italian</title>
    </book>
</bookstore>

输出:

bookstore [id="root"]
├── book [id="book1", category="COOKING"]
│   └── title [id="title1", lang="en"]
│       └── "Everyday Italian"

说明:两种格式均可,推荐使用树形字符以获得更好的可视化效果。显示类命令不改变文件状态,不进入撤销栈

2.3 拼写检查命令

1. spell-check - 拼写检查

spell-check [file]

功能:检查文本文件、xml文件中的拼写错误。

参数说明:

  • 不指定参数:检查当前活动文件
  • file:检查指定文本文件

输出格式参考:

拼写检查结果:
第1行,第5列: "recieve" -> 建议: receive
第3行,第12列: "occured" -> 建议: occurred
拼写检查结果:
元素 title1: "Itallian" -> 建议: Italian
元素 author2: "Rowlling" -> 建议: Rowling

About

高级软件开发技术小组作业

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages