分享一個LiteDB做的簡單考試系統輔助工具


    凌晨,被安排在公司值班,因為台風“燦鴻”即將登陸,風力太大,辦公樓,車間等重要部分需要關注。所以無聊,那就分享一下,今天給朋友臨時做的一個小的考試系統輔助工具吧。其實非常小,需求也很簡單,但是可以根據實際需要進行擴充,暫時只實現了一些核心功能。界面丑了點,無所謂,湊合着用吧。

1.考試系統輔助需求

    上午10點一個朋友緊急求助,單位要進行在線測評,開卷考試,題庫以及答案已經發給他們了,但是太多,好幾百道題目,翻資料都來不及。問我能不能做一個軟件,能夠快速填充答案或者找到題目,節省時間,提高准確率。經過半個小時的QQ溝通,基本明確了大概要做的,由於時間緊急,晚上就要用,盡量搞簡單的吧。總結下來,有這么幾個需求:

  1. 要能快速導入題庫,幾百道題,手動添加得多長時間,不敢想象,再說了,會寫點程序就是要減輕工作量;

  2. 要盡量自動化,直接填充答案是不可能了,不是技術上不可行,是時間來不及,在線考試的頁面都沒見到;

  3. 題庫類型比較多,有填空題,單選題,多選題以及判斷題,要盡量區分,第一時間找到原題和答案;

2.簡單思考后的思路

    拿到需求,雖然長時間沒搞WinForm,但簡單的東西還是會的。腦子里第一個飄出的思路就是:

  1. 使用Aspose.Words快速導入題庫,成熟好用,以前接觸過一點點;

  2. 由於給定的題庫和答案已經有了,暫時就不用區分題目類型了,直接找到題目就找到了答案,然后手動填寫就好了

  3. 題庫數據用什么保存?顯然用啥MSSQL,Mysql都是扯淡了,那Sqlite呢?No,No,No,前不久剛剛接觸過開源的.NET NoSQL數據庫LiteDB,為啥不嘗鮮呢?使用文章看這里:

               .NET平台開源項目速覽(3)小巧輕量級NoSQL文件數據庫LiteDB 

               .NET平台開源項目速覽(7)關於NoSQL數據庫LiteDB的分頁查詢解決過程

       4.怎么更快的找到題目呢?難道要手動復制,再粘貼到界面搜索?有點慢,那就先省略一步吧,復制題干部分文字后,直接點界面搜索,然后自動獲取剪切板的數據,自動進行搜索,也就是1個題目要2次操作,復制和點擊搜索。雖然想到了 監視剪切板,但第一次的思路里面考慮到時間,就沒往下想。

       5.如何搜索呢?直接暴力點吧,也就幾百道題,就算幾千道也應該不成問題,直接題干字段對比是否包括 選擇的文字部分,然后搜索,彈出相應的題目;

  6.考慮到有可能一些文字(復制題干的時候不是全部復制,部分復制就可以了)包含在多個題目中,要至少考慮2個吧,否則就題目復制的時候稍微多一點,減少重復的概率;

  雖然上面是簡單的思路,但實際上最終不完全是這樣子,經過實際的修補,不斷調整方向,有一些改動:

  1.Aspose.Words還是不要用了,過程處理有點繁瑣,直接搞一個導入題庫的界面,文本框和按鈕,將題目手動復制進去,幾分鍾都不需要的事情,何必用Aspose.Words搞來搞去;

  2.雖然區分類型很簡單,開始打算不做,后面還是做了,因為也的確比較簡單吧;

3.實現過程與代碼

    為配合LiteDB數據庫的操作,寫了個簡單的題目實體類,包括簡單幾個字段,如下所示代碼:

public class Problem
{
	/// <summary>題目編號,可以重復,注意不能使用Id</summary>
	public Int32 ProbId { get; set; }
	/// <summary>整個題目內容,同時包括選項</summary>
	public String ProbText { get; set; }
	/// <summary>答案,一般是題干答案中的東西</summary>
	public String Answer { get; set; }
	/// <summary>題目類型</summary>
	public String TypeName { get; set; }
}

3.1 實現過程之數據導入

    數據導入界面很簡單,選擇題目類型,然后復制進去,因為已經拿到Word版的題庫了,很規則,由於朋友單位內部資料,題庫不分享,自己到百度找了一個差不多格式的試卷,如下:

    復制的時候,直接復制題目(要包括編號)即可,前面的 單項選擇題,類型就不要復制了,手動選一下吧。看看界面:

    代碼很簡單,唯一要注意的是,對於選擇題來說,如何區分不同題目是個難點,開始沒注意這個問題,因為填空題以及判斷題都可以通過換行符來確定,換行了就是一道新題目。但是這個選擇題就尷尬了,有幾次換行,而且還不固定,最后觀察到題目的編號是一個可以利用的東西,每一次題目前都是 數字+、號,用這個來區分吧。看看實現代碼,這是最終的版本,中間修改的就不說了,涉及到LiteDB的簡單操作,可以參考上面的文章或者下面的核心導入按鈕的代碼:

//獲取題目類型
var typeName = comboBox1.Text.Trim();
using (var db = new LiteDatabase("Prob.db"))
{    
	var col = db.GetCollection<Problem>("Problem");
	//先換行分割
	var prolist = textBox1.Text.Trim().Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
	Problem model ;
	Int32 index = 0;
	//題目列表,多行的每次讀最后一個
	List<Problem> modelList = new List<Problem>();
	//根據每行的第一個字符和、號進行區分,如果不是新題目,就作為選項添加到上一個題目中去
	while (index <prolist.Length )
	{
		var item = prolist[index];
		if (String.IsNullOrEmpty(item)) continue;
		var titles = item.Trim().Split('、');
		if (titles.Length > 0)
		{
			int number;
			if(Int32.TryParse(titles[0],out number))
			{   //是數字,添加
				model = new Problem();  //如果分割第1個是數字,則說明是1個新的題目
				model.ProbId = number;
				model.ProbText = item;
				model.TypeName = typeName;
				//確定答案,答案在()里面,把括號里面是字符串分解並組合
				var ans = item.Split('(', ')');
				if (ans.Length < 2) model.Answer = string.Empty;
				if (ans.Length > 1) model.Answer = ans[1];
				if (ans.Length > 3) model.Answer = ans[1] + "\r\n" + ans[3];
				modelList.Add(model);
			}
			else
			{
				//不是數字,就添加到前一個實體中去,並更新題目內容
				modelList.Last().ProbText += ("\r\n" + item);
			}
		}
		else
		{
			//不是數字,就添加到前一個實體中去,並更新題目內容
			modelList.Last().ProbText += ("\r\n" + item);
		}
		index++;
	}
	col.InsertBulk(modelList);
}

    當然每個人遇到的題庫格式不一樣,自己在這里修改唄。。。我也沒時間做個通用的。。反正是給朋友幫忙的東西。順便分享給大家解決問題的思路。

3.2 實現過程之搜索主界面

    搜索界面也是很簡單,第一次實現的時候沒有考慮監視ctrl+c,只是想點擊搜索,自動獲取剪切板內容進行搜索。再就是要注意多條記錄的問題,只顯示2條吧,如果找不到相同的,就復制多一點題目部分。每一個題目顯示題目類型,答案,如果為空就顯示手動選擇檔案。界面如下:

    核心代碼如下:

//每次搜索前都要清空其他控件
txtDisp1.Text = String.Empty;
txtDisp2.Text = String.Empty;
lblAnswer1.Text = String.Empty;
lblAnswer2.Text = String.Empty;
try
{
	//獲取剪切板數據
	IDataObject iData = Clipboard.GetDataObject();
	if (iData.GetDataPresent(DataFormats.Text)) txtKeyValue.Text = ((String)iData.GetData(DataFormats.Text)).Trim();
	//查找滿足關鍵詞的題目
	var models = list.Where(n => n.ProbText.Contains(txtKeyValue.Text)).ToList();
	//最多只顯示2個包括選擇關鍵詞的題目
	if (models.Count == 1)
	{
		lblTypeName1.Text = models[0].TypeName;
		lblAnswer1.Text = String.IsNullOrEmpty(models[0].Answer) ? "題目尋找答案" : models[0].Answer;
		txtDisp1.Text = models[0].ProbText;
	}
	if (models.Count >= 2)
	{
		lblTypeName1.Text = models[0].TypeName;
		lblAnswer1.Text = String.IsNullOrEmpty(models[0].Answer) ? "題目尋找答案" : models[0].Answer;
		txtDisp1.Text = models[0].ProbText;

		lblTypeName2.Text = models[1].TypeName;
		lblAnswer2.Text = String.IsNullOrEmpty(models[1].Answer) ? "題目尋找答案" : models[1].Answer;
		txtDisp2.Text = models[1].ProbText;
	}
}
catch(Exception err)
{
	MessageBox.Show("出現錯誤,重啟后再試!");
}

3.3 監視復制Ctrl+C

    因為還有時間,所以就想盡量改善點,然后有去百度如何 監視剪切板,貌似要用到win api,學藝不精,搞不來,看到一篇文章監視ctrl+c,其實也是個不錯的方案,一步步來嗎,哪位童鞋有C# 直接監視剪切板的 代碼,希望分享一下。

    我用到的代碼來源於這里:http://www.cnblogs.com/over140/archive/2007/11/05/934452.html

    自己稍微修改下就OK了,不詳細解釋了。

3.4 塞翁失馬-手賤的教訓

    這個完整的東西前后共用了大約4個小時整的時間。坑爹的是中午寫的第一版代碼,讓我手動刪除目錄給刪錯了。。。后來改進的時候,無奈又重新寫了一下,其實思路都有,代碼比較簡單。

以前習慣頻繁更新SVN的,這次失手,算教訓吧,幸好不是啥重要代碼。另外,塞翁失馬焉知非福,重新寫也有好處,不用看改得亂七八糟的代碼,一次到位,代碼思路也清晰多了,初版本代碼這里有的是測試,臨時使用。。。等等。算是一種安慰吧。

4.使用方法

    使用方法這再次簡單介紹一下,先手動復制題庫到導入界面,進行題庫導入;然后啟動進行搜索,可以將在線考試的頁面和該軟件同時打開,分屏放置,通過Ctrl+C或者手動復制,搜索也可以進行,查詢到題目后,對照答案填寫,然后循環使用。如果沒有答案,那就看原題,原題一般是有答案的,除非格式有變動,無法轉換。

5.資源

    代碼托管到Github吧,我不會告訴你地址在這里的:https://github.com/asxinyu/ExamSystem/

    不知不覺,抽了幾根煙,已經過去1個多小時了,“燦鴻”已經越來越近,辦公樓窗戶以及牆體滲水一塌糊塗,去搶險去了,搶險完了,要去睡覺,希望醒來的時候,看到大伙的點贊呢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM