题库自动生成器

:::tip 想做一个将文本处理的脚本,每次期末复习的效果感觉有点不好,在高中的时候,每天做题,感觉这样的效果会比较好一点。所以就想着能不能将期末的复习题文档转换成题库的形式。 :::

# 需求

需求很简单,给一个带有很多题的文档,然后这个应用可以将文档转换成题库,例如:假如是一道选择题,应用可以将这道选择题分解开,选项变成可以选择的按钮,假如选的是正确的答案,这个选项会有提示正确,反之则提示错误。以后努力可以支持更多的题型。

# 分析与设计

暂时先不考虑文档的多样性。文档以txt格式,且格式是固定的。

我觉得最难的在于如何将这一道道选择题区分开,如何将选项区分开,如何提取出选项,题号,选项号,下面就围绕上述几个问题展开分析一下。

:::tip 35.在系统间提供可靠的数据传输的层次是(D)。 A、物理层 B、数据层 C、网络层 D、传输层 ::: 例如我现在拿到这样一道题,首先肯定是要取出35、D、A、B、C、D这些可以唯一确定一道题类似ID。首先要对文档要做的就是去除空格,空格又两种,中文和英文,需要注意;其次就是需要统一括号,希望就是默认的答案就在括号里,这样可以简化寻找答案。

# 如何区分每一道题

首先要做的就是如何区分每一道题,一道题是作为一个独立的个体,必须要区分正确。每一道题的开头的都是(数字点非数字),可以以此简单的区分每一道题。代码也正是这样做的,写一个正则公式,找到所有的题号,就可以找到每道题起始的位置,则可以划分每一道题。

正则公式((\\d{1,}|[一二三])[.|、|.]{1}\\D),对应于上边的数字点非数字。一道题就是一个独立的单位,保存在ArrayList中。

实现代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void Divition() {
	flag = 0;
	String regex = "((\\d{1,}|[一二三])[.|、|.]{1}\\D)";
	pattern = Pattern.compile(regex);
	matcher = pattern.matcher(str);
	int index=-1;
	int end=-1;
	while(matcher.find()) {
		String s = matcher.group();
		end=matcher.end()-2;//代表点的位置
		if(s.charAt(0)=='一'||s.charAt(0)=='二'||s.charAt(0)=='三') {
			flag=0;
		}
		else {
			if(str.substring(end-flag-(s.length()-2), end).compareTo(limit[flag])>0) {
				flag++;
			}			
    	}
		if(index!=-1) {
			whole.add(str.substring(index, end-flag-(s.length()-2)));
		}
		index = end-flag-(s.length()-2);//解决第一道题不是个位数只取一位问题
	}
	end = str.length();
	whole.add(str.substring(index,end));
}

# 在一道题中提取题目

我觉得可以以遇到A为判断条件,但是这个A不能是题目中的单词,不能是括号中的正确答案。

在提取题目之前,我首先提取了答案,然后删掉这段答案,要不然在搜索的时候很可能与选项混淆。然后再找到第一个选项A的位置,在此之前的都是题目,正常在题目中是不会出现单独字母的(如果出现了,现在解决不了),所以只需要保证一个字母的前后都不是字母,就可以解决了。这是对于选择题来说的,对于判断题只需要删除掉正确答案就可以了。提取完题目就删掉题目了,只剩下选项了。

正则公式:([ABCDABCDabcd]{1}[.|、|.]{1})

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public String Subject(String s) {
	String regex_T = "([ABCDABCDabcd]{1}[.|、|.]{1})";
	Pattern pattern = Pattern.compile(regex_T);
	Matcher matcher = pattern.matcher(s);
	int index = -1;
	if (matcher.find()) {
		index = matcher.start();
	}
	position = index;
	String cache = s.substring(0, index);
	return cache;
}

# 在题目中提取正确答案

正确答案就在括号中,所以只需要找到括号,然后取出里面的字符就可以了。

正则公式:(\\([ABCDABCDabcd]\\)),跟之前寻找A选项正好相反。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public String Result(String s) {
	String regex_R = "(\\([ABCDABCDabcd]\\))";
	Pattern pattern = Pattern.compile(regex_R);
	Matcher matcher = pattern.matcher(s);
	if (matcher.find()) {
		T += matcher.group().toString().charAt(1);
	}
	s = s.replaceAll(regex_R, "");
	return s;
}

# 提取出每一个选项

题目提取完了,那就该提取选项了,碰到字母,并且这个字母是单独存在的。因为现在一道题中只剩下了选项,所以只需要找字母加上点或顿号等。

正则公式:([ABCDABCDabcd]{1}[.|、|.]{1})

判断题代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public String Option(String s) {
	// TODO Auto-generated method stub
	String cache="";
	cache += "<input type=\"radio\" id=\""+ID+"-"+"T"+"\" name=\"xxx\" />";
	cache += "T";
	cache += "\n<input type=\"radio\" id=\""+ID+"-"+"F"+"\" name=\"xxx\" />";
	cache += "F";
	cache += "\n<br />\n";
	cache += "</form>\n";
	return cache;
}

选择题代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public String Option(String s) {
	String regex_X = "([ABCDABCDabcd]{1}[.|、|.]{1})";
	Pattern pattern = Pattern.compile(regex_X);
	Matcher matcher = pattern.matcher(s);
	int index = -1;
	String cache = "";
	int end = -1;
	while (matcher.find()) {
		end = matcher.start();
		if (index != -1) {
			cache += "<input type=\"radio\" id=\"" + ID + "-" + s.charAt(index) + "\" name=\"xxx\" />";
			cache += s.substring(index, end);
			cache += "\n<br />\n";
		}
		index = end;
	}
	end = s.length();
	cache += "<input type=\"radio\" id=\"" + ID + "-" + s.charAt(index) + "\" name=\"xxx\" />";
	cache += s.substring(index, end);
	cache += "\n<br />\n";
	cache += "</form>\n";
	return cache;
}

# 实现类图

采用java语言,用正则对文本进行判断,切割。

Handle+ str: String=""+ str: String=""+ out: String=""+ out: String=""~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null~ whole: ArrayList<String> ~ whole: ArrayList<String> + Handle(s: String)+ Handle(s: String)+ Init(): void+ Init(): void+ Divition(): void+ Divition(): void+ merge(): void+ merge(): voidPrint~ file: String=null~ file: String=null+ Print(file: String)+ Print(file: String)+ out():void+ out():voidChoice+ choice: Choice+ choice: Choice~ str: String=null~ str: String=null~ ID: String=null~ ID: String=null+ out: String=""+ out: String=""~ T: char~ T: char~ position: int~ position: int~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): St…+ Option(s: String): String+ Option(s: String): String+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): StringJudgement+ judgement: Judgement+ judgement: Judgement+ ID: String=null+ ID: String=null+ str: String=null+ str: String=null~ T: char~ T: char+ out: String=""+ out: String=""~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): St…+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): StringClassification~ T: char~ T: char~ cache: String=null~ cache: String=null+ Result(s: String): void+ Result(s: String): void+ Function(s: String): String+ Function(s: String): Stri…Connect+ file: String=null+ file: String=null~ reader: BufferedReader=null~ reader: BufferedReader=null+ Connect(file: String)+ Connect(file: String)+ con(): void+ con(): voidMainMainUseUse«interface»Mold+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): Str…+ Option(s: String): String+ Option(s: String): String+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): StringUseUseUseUseUseUseUseUseUseUseViewer does not support full SVG 1.1

# 重构后的实现类图

发现有点不符合开闭原则,代码重构后去除了classification类,这样把自己的特征都写到自己的类中。特征在Rule()方法

Handle+ str: String=""+ str: String=""+ out: String=""+ out: String=""~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null~ whole: ArrayList<String> ~ whole: ArrayList<String> + Handle(s: String)+ Handle(s: String)+ Init(): void+ Init(): void+ Divition(): void+ Divition(): void+ merge(): void+ merge(): voidPrint~ file: String=null~ file: String=null+ Print(file: String)+ Print(file: String)+ out():void+ out():void+ Remove(file: String): String+ Remove(file: String): Stri…Choice+ choice: Choice+ choice: Choice~ str: String=null~ str: String=null~ ID: String=null~ ID: String=null+ out: String=""+ out: String=""~ T: char~ T: char~ position: int~ position: int~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): St…+ Option(s: String): String+ Option(s: String): String+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): String+ Rule(s: String): String+ Rule(s: String): StringJudgement+ judgement: Judgement+ judgement: Judgement+ ID: String=null+ ID: String=null+ str: String=null+ str: String=null~ T: char~ T: char+ out: String=""+ out: String=""~ pattern: Pattern=null~ pattern: Pattern=null~ matcher: Matcher=null~ matcher: Matcher=null+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): St…+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): String+ Rule(s: String): String+ Rule(s: String): StringConnect+ file: String=null+ file: String=null~ reader: BufferedReader=null~ reader: BufferedReader=null+ Connect(file: String)+ Connect(file: String)+ con(): void+ con(): voidMainMainUseUseUseUseUseUseUseUse«interface»Mold+ run(s: String): String+ run(s: String): String+ TitleNumber(s: String): String+ TitleNumber(s: String): Str…+ Option(s: String): String+ Option(s: String): String+ Subject(s: String): String+ Subject(s: String): String+ Result(s: String): String+ Result(s: String): String+ onClick(): String+ onClick(): String+ Rule(s: String): String+ Rule(s: String): StringViewer does not support full SVG 1.1

# 开发环境的搭建

暂时先在本地测试

# 总结

考虑到以后可能会有其他题型添加,就把一些功能分开写了,这样就可以很好的对程序扩充了;有的文档不仅有选择题还有判断题,先开始我是以题号作为ID的,最后我把判断和选择放到一起的时候,就出现了ID重复的问题,所以就用1-代表判断题,2-代表选择题,就可以解决不同题型ID重复的问题了,相同题型,假如说题号一样的话,就会出问题了,在现在这个代码还解决不了这样的问题。

# 样例

# 一、判断

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

# 二、多项选择题(共61小题)

确定

确定

确定

确定

确定

# 三、单选题

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定

确定