王 東,林 宏
(貴州師范學(xué)院 數(shù)學(xué)與大數(shù)據(jù)學(xué)院,貴州 貴陽 550018)
試題錄入技術(shù)是試題庫系統(tǒng)研究的關(guān)鍵內(nèi)容,試題庫建設(shè)質(zhì)量和效率很大程序上依賴于試題錄入方式和采用的技術(shù)。試題庫系統(tǒng)通過提供專門的試題錄入互交界面,輸入或設(shè)置試題屬性及內(nèi)容,再提交保存到服務(wù)器端數(shù)據(jù)庫中。這是一種單題錄入的方式,每次錄入一道試題,逐題保存在試題庫中。采用這種錄入方式建設(shè)題庫的速度較慢、效率較低。一些研究者關(guān)注到這一點(diǎn),致力于研究一種高效、便捷的試題批量導(dǎo)入方法,通過程序自動(dòng)識(shí)別、抽取、錄入各類試題信息,將人力從枯燥、乏味的手工錄入試題信息的工作中解放出來。
已有的典型研究主要包括以下幾個(gè)方面:(1)借鑒詞法、語法分析技術(shù)解析試題內(nèi)容實(shí)現(xiàn)試題導(dǎo)入,如文獻(xiàn)[1]基于詞法、語法分析的試題導(dǎo)入系統(tǒng)的研究;(2)通過構(gòu)建有嚴(yán)格控制要求的試題導(dǎo)入文檔模版實(shí)現(xiàn)試題自動(dòng)識(shí)別,如文獻(xiàn)[2]基于PB與VBA的試題導(dǎo)入方法研究、文獻(xiàn)[3]基于VFP實(shí)現(xiàn)批量導(dǎo)入Word文檔試題到數(shù)據(jù)庫的方法,文獻(xiàn)[4]基于ASP.NET將Word試題逐題導(dǎo)入數(shù)據(jù)庫的實(shí)現(xiàn)方法,文獻(xiàn)[5]通過解析試題模型得到 HTML 格式,再根據(jù) HTML 的標(biāo)簽信息分解出題目與答案;(3)利用解析引擎從試題文檔結(jié)構(gòu)進(jìn)行試題內(nèi)容分析的方法,如文獻(xiàn)[6]的基于PANTLR的試卷識(shí)別和導(dǎo)入系統(tǒng)的研究。上述試題導(dǎo)入方法取得一定的效果,同時(shí)也存在一些問題,如有些方法對(duì)試題文檔格式限制過嚴(yán),影響易用性;有的方法丟失了部分試題信息,如格式信息;有些方法識(shí)別準(zhǔn)確率較低、使用要求高等。本文以廣泛使用的word文檔為導(dǎo)入數(shù)據(jù)源,提出一種簡(jiǎn)單、高效、穩(wěn)定的批量導(dǎo)入方法。
一個(gè)試題庫系統(tǒng)包含多個(gè)題庫,每個(gè)題庫對(duì)應(yīng)一門學(xué)科或一門課程,負(fù)責(zé)管理若干試題。在存儲(chǔ)試題數(shù)據(jù)時(shí),采用怎樣的存儲(chǔ)方式與試題錄入、編輯方式密切相關(guān),同時(shí)對(duì)后續(xù)試題應(yīng)用如組卷、導(dǎo)出等產(chǎn)生直接影響。通常,存儲(chǔ)試題信息時(shí),一道試題信息由多維屬性來表征,如試題編碼、題干、答案、所屬題型、難度系數(shù)、所屬知識(shí)點(diǎn)等,對(duì)題干和答案的存儲(chǔ)策略最為重要。最常采用的存儲(chǔ)方式是將試題題干和答案的html代碼以字符串形式存儲(chǔ)到數(shù)據(jù)庫表的相應(yīng)字段中,該方式存在的問題是描述包含圖文、表格、公式等復(fù)雜對(duì)象混排效果的試題較為困難,需要將試題組卷生成word文檔時(shí)會(huì)丟失原有排版效果,難以生成一份理想的word文檔。第二種存儲(chǔ)方式是將試題以word格式的文檔形式存放到服務(wù)器上,或者直接將試題以二進(jìn)制流形式存儲(chǔ)到數(shù)據(jù)庫中。若試題以word文檔存放在服務(wù)器上,數(shù)據(jù)庫中需要記錄試題路徑信息;若以二進(jìn)制流存儲(chǔ)到數(shù)據(jù)庫中,使用時(shí)需要將二進(jìn)制流解析成word文檔。該存儲(chǔ)方式有利于保持試題格式在試題錄入、組卷等環(huán)節(jié)中的一致性,但在web頁面上呈現(xiàn)試題信息時(shí)要額外進(jìn)行word到html的格式轉(zhuǎn)換處理。
基于C/S體系結(jié)構(gòu)的試題庫管理系統(tǒng),利用RichTextBox控件能較好顯示編輯圖文并茂的試題[7-9],基于B/S體系結(jié)構(gòu)的試題庫管理系統(tǒng),試題錄入的常規(guī)方法是使用web富文本編輯器,如:eWebEditor,KindEditor,CKEditor,ueditor等,富文本編輯器提供所見即所得的功能,操作人員可以在直觀、易用的界面創(chuàng)建和編輯試題內(nèi)容,試題內(nèi)容最終是以html字符串形式存儲(chǔ)。使用富文本編輯器對(duì)純文本試題編輯比較方便,然而對(duì)于包含圖片、表格和公式的試題編輯,操作上則比較繁瑣,圖片需要單獨(dú)上傳到服務(wù)器上,實(shí)現(xiàn)圖文混合排版很耗時(shí),僅部分富文本編輯器有限支持word內(nèi)容的直接復(fù)制和粘貼,大多數(shù)富文本編輯器不能將上傳的word文檔直接解析成html格式,也未提供將html格式內(nèi)容導(dǎo)出為word文檔的功能。若試題庫系統(tǒng)需要生成word格式的試卷,或者試題要以word文檔形式存放在服務(wù)器上,則可以使用Office的ActiveX控件作為編輯器,如微軟的dsoframer開源控件[10-11],將ActiveX控件嵌入到網(wǎng)頁中,通過組件模型提供的API實(shí)現(xiàn)對(duì)word文檔的操縱,該方式存在的主要問題是多用戶并發(fā)操作時(shí),服務(wù)器端容易出錯(cuò)。相對(duì)而言,由第三方提供的Office組件,如Pageoffice,則比較完美的提供了在線編輯和處理的功能。
試題批量導(dǎo)入方法與試題存儲(chǔ)策略和試題編輯方法有緊密聯(lián)系。在試題編輯方法選擇上,要確保批量導(dǎo)入的試題能夠再次通過單題錄入編輯器進(jìn)行編輯,并且不會(huì)產(chǎn)生任何信息丟失和格式錯(cuò)亂。對(duì)于試題存儲(chǔ)策略,以獨(dú)立文檔形式將試題存放在服務(wù)器上,每道試題由題干和答案兩個(gè)word文檔構(gòu)成,這種存儲(chǔ)方式完整保留了試題的排版和格式信息,便于后續(xù)的試題使用。除了存儲(chǔ)題干和答案信息外,完整的試題信息還需要其它屬性加以描述,如試題所屬章節(jié)、試題難度系數(shù)、試題所屬題型、題干路徑、答案路徑、空數(shù)等,這些屬性值也需要存儲(chǔ)到數(shù)據(jù)庫中。當(dāng)采用批量導(dǎo)入時(shí),如果單純追求自動(dòng)化程度,則需要將每道試題的所有屬性都附加到導(dǎo)入文檔中,在導(dǎo)入時(shí)通過程序自動(dòng)識(shí)別出來,但這樣不僅增大待導(dǎo)入文檔的預(yù)處理難度,而且試題自動(dòng)識(shí)別的復(fù)雜度也很高,必然降低了批量導(dǎo)入功能的易用性和實(shí)用性。因此,批量導(dǎo)入時(shí)需要在人工干預(yù)和程序自動(dòng)處理之間做折中,將部分屬性通過導(dǎo)入界面進(jìn)行預(yù)設(shè)置,如:試題所屬章節(jié)、難度系數(shù)。當(dāng)批量導(dǎo)入時(shí),增加一個(gè)工人確認(rèn)環(huán)節(jié),在最終導(dǎo)入系統(tǒng)前可以對(duì)屬性進(jìn)行個(gè)別調(diào)整和確認(rèn)。試題批量導(dǎo)入流程如圖1所示:
圖1 批量導(dǎo)入流程
首先,通過客戶端將試題文檔上傳到服務(wù)器上,對(duì)試題進(jìn)行自動(dòng)解析與識(shí)別,將試題與答案分割成獨(dú)立的word格式文檔,其次,將試題信息存儲(chǔ)到臨時(shí)數(shù)據(jù)庫,用戶再對(duì)導(dǎo)入試題進(jìn)行確認(rèn),最后,存入題庫數(shù)據(jù)庫。下面對(duì)試題導(dǎo)入過程進(jìn)行詳細(xì)討論。
要對(duì)完全自由格式的文檔進(jìn)行自動(dòng)識(shí)別是極其困難的。因此,對(duì)待導(dǎo)入的文檔制作了模板,設(shè)置適當(dāng)規(guī)則。試卷導(dǎo)入模板要力求簡(jiǎn)單、規(guī)則明了,使用者在已有試卷基礎(chǔ)上稍做整理即可。制定了三條規(guī)則,規(guī)則1,使用“##試題類型名稱”的單行文本作為不同題型區(qū)域的分割標(biāo)記,這條規(guī)則可以確保一次能導(dǎo)入多種題型的試題;規(guī)則2,每道試題包括試題題干區(qū)域與答案區(qū)域兩部分,兩部分區(qū)域按序相鄰,題干區(qū)域與答案區(qū)域用“@@”分隔,“@@”獨(dú)立成行,試題與試題之間也用獨(dú)立成行的“@@”分隔;規(guī)則3,填空題中的“空”用帶下劃線的連續(xù)空格表示,這條規(guī)則確保能自動(dòng)識(shí)別出填空題中的空數(shù)。試卷導(dǎo)入的模板格式如圖2所示。
圖2 試題導(dǎo)入模板
對(duì)導(dǎo)入文檔進(jìn)行自動(dòng)處理的第一步是根據(jù)規(guī)則1獲取文檔中的所有題型分隔標(biāo)識(shí)“##”,取得與標(biāo)識(shí)符“##”在同一段落的文本(該文本為題型類型名稱),同時(shí)獲得各題型的區(qū)域范圍。對(duì)word導(dǎo)入文檔的訪問與操縱技術(shù),采用微軟提供的組件對(duì)象模型(COM組件),通過在開發(fā)環(huán)境中引用Office類型庫,就可以訪問Office編程接口和各種對(duì)象。要對(duì)文檔中特定模式進(jìn)行搜索,可以使用word對(duì)象模型中Range對(duì)象的Find.Execute方法,該方法能搜索特定模式串在文檔中的所有出現(xiàn)位置。通過使用該方法迭代搜索題型分隔標(biāo)識(shí)在文檔中出現(xiàn)的位置,精準(zhǔn)分割出每種題型所屬的試題區(qū)域范圍,并使用一個(gè)ArrayList對(duì)象存儲(chǔ)試題類型信息,ArrayList對(duì)象中的每個(gè)結(jié)點(diǎn)記錄了每種題型的名稱、區(qū)域起始位置及區(qū)域終點(diǎn)位置。試題類型區(qū)域識(shí)別的詳細(xì)步驟如下:
輸入:試題文檔Range對(duì)象myrange
輸出:題型列表qtypelist
定義:struct {qtype, start ,end},/* qtype:題型名稱, start:起始位置,end:結(jié)束位置 */
1.初始化:qtypelist←new ArrayList
/*獲取數(shù)據(jù)庫已有試題類型*/
2.dv←db.RunSelectSQL(SQLstr)
3.end1←myrange.end /*文檔尾部位置*/
/*執(zhí)行myrange.Find.Execute方法,迭代搜索題型標(biāo)識(shí)符,直到該方法返回值為零*/
4.while flag=true do
5.flag ←myrange.Find.Execute("##",…)
6.if flag=true then
/*獲取從文檔起始位置到當(dāng)前搜索點(diǎn)范圍內(nèi)所有段落數(shù)*/
7.x←G_wa.ActiveDocument.Range(0, myrange.end).Paragraphs.Count
/*獲取搜索點(diǎn)所在段落的文本內(nèi)容,即題型類型名稱*/
8.newrange←G_wa.ActiveDocument.Range(0, myrange.End).Paragraphs(x).Range
/*遍歷數(shù)據(jù)庫內(nèi)已設(shè)置的題型類別,若獲取到的題型類型是合法題型,將該題型類型名稱、該題型起始位置及區(qū)域終點(diǎn)位置信息追加到題型列表中。*/
9.fori=0 in dv.Count-1
10.ifnewrange.text=dv.Item(i) then
11.node.qtype←dv.Item(i);node.start←myrange.Start; node.end1←end1
12.ifqtypelist.Count!=0 then
13.qtypelist.Item(qtypelist.Count-1).end←myrange.Start
14.endif
15.qtypelist.Add(node)
16.endif
17.endfor
18.endif
19.endwhile
使用Find.Execute方法對(duì)全文進(jìn)行一次搜索后,獲得題型列表。識(shí)別各題型內(nèi)的試題時(shí)需要再對(duì)全文進(jìn)行第二次搜索,根據(jù)規(guī)則2以“@@”為模式進(jìn)行搜索,在搜索中逐題分割出試題與答案區(qū)域,并為識(shí)別出的試題與答案區(qū)域設(shè)置書簽,生成試題答案列表,列表中每個(gè)結(jié)點(diǎn)包含4個(gè)信息:試題類型名稱、書簽名稱、區(qū)域起點(diǎn)位置、區(qū)域終點(diǎn)位置。具體算法描述如下:
輸入:試題文檔Range對(duì)象myrange,題型列表qtypelist
輸出:試題列表questionlist
定義:struct {qtype,bookmarks,start,end },/*試題類型名稱、書簽名稱、區(qū)域起點(diǎn)位置、區(qū)域終點(diǎn)位置*/
1.初始化:questionlist←new ArrayList
2.while flag=true do
3.flag=myrange.Find.Execute("@@",…)
4.if flag=false then break;
/*遍歷題型列表,判斷試題位置區(qū)域與試題類型區(qū)域的位置關(guān)系*/
5.fori=0 in qtypelist.Count-1
6.node1←qtypelist.Item(i)
/*若試題分割標(biāo)記位于當(dāng)前題型位置范圍內(nèi)*/
7.ifmyrange.Start>=node1.start and myrange.Start<=node1.end1 then
/*若當(dāng)前標(biāo)記與前一標(biāo)記屬同一題型, 試題起始位置為前一標(biāo)記結(jié)束位置,試題結(jié)束位置為當(dāng)前標(biāo)記開始位置,否則,新題型開始的第一題,試題起始位置為題型起始位置,試題結(jié)束位置為當(dāng)前標(biāo)記開始位置*/
8.ifquestiontype=node1.qtype then
9.start←prestart,end1←myrange.Start
10.else
11.start←node1.start+1
12.end1←myrange.Start
13.end if
/*設(shè)置試題范圍*/
14.newrange←G_wa.ActiveDocument.Range(start,end1)
/*設(shè)置書簽*/
15.G_wa.ActiveDocument.Bookmarks.Add("PO_" & k,newrange)
/*新增試題結(jié)點(diǎn)*/
16.questionnode.qtype←node1.qtype
17.questionnode.bookmarks← "PO_" & k
18.questionnode.start←start
19.questionnode.end1←end1
20.questionlist.Add(questionnode)
21.k←k+1
22.prestart←myrange.End+1
23.questiontype←node1.qtype
24.endif
25.endfor
26.endwhile
填空題與其它題型不同,填空題需要存儲(chǔ)試題的空數(shù)信息。批量導(dǎo)入方法應(yīng)具備填空題“空”的自動(dòng)檢測(cè)能力。為實(shí)現(xiàn)該功能,首先需要對(duì)試題文檔中的填空題按規(guī)則3做規(guī)范化處理,填空題中的每空均用帶下劃線的連續(xù)空格串表示,批量導(dǎo)入時(shí)再由處理程序?qū)μ羁疹}的“空”進(jìn)行自動(dòng)識(shí)別。具體算法描述如下:
輸入:題干Range對(duì)象oRng
輸出:空數(shù)
變量:字符下劃線狀態(tài)列表oDic,空數(shù)列表arrItems,空格blank
1.獲取試題題干文本字符長(zhǎng)度,sLen←oRng.Characters.Count
/*遍歷所有字符,存儲(chǔ)所有字符的下劃線狀態(tài)*/
2.for j=1 in sLen
3.oDic←oRng.Characters(j).Font.Underline
/*遍歷下劃線狀態(tài)列表,存儲(chǔ)所有的帶下劃線連續(xù)空格串的開始位置*/
4.for j=0 in oDic.Count-2
ifoDic.Item(j)=wdUnderlineNone and oDic.Item(j+1)=wdUnderlineSingle and oRng.Characters(j+1)=blank then
arrItems←(j+2)
endif
endfor
5.返回空格數(shù):arrItems.count
批量導(dǎo)入的最后處理是將包含特殊書簽的文檔分割生成一個(gè)個(gè)獨(dú)立的子文檔。該過程分兩步處理,第一,根據(jù)試題識(shí)別環(huán)節(jié)標(biāo)記的特殊書簽,將每一個(gè)書簽代表的區(qū)域內(nèi)容生成一個(gè)獨(dú)立的word文檔,首先將書簽內(nèi)容復(fù)制到剪貼板,再將剪貼板內(nèi)容存儲(chǔ)為word文件,并返回文件的路徑信息;第二,將每道題的屬性信息存入數(shù)據(jù)庫試題臨時(shí)表中,并觸發(fā)確認(rèn)頁面供用戶干預(yù),用戶可對(duì)試題所屬章節(jié)、試題難度系數(shù)等屬性做進(jìn)一步修改,再將試題最終提交到數(shù)據(jù)庫中??紤]到多用戶并發(fā)處理的情形,存儲(chǔ)到臨時(shí)表時(shí)為每個(gè)用戶生成一個(gè)時(shí)間戳,從臨時(shí)表讀取數(shù)據(jù)時(shí)根據(jù)相應(yīng)的時(shí)間戳,就能避免產(chǎn)生干擾和沖突。文檔分割具體算法描述如下:
輸入:書簽列表arrlist, 題型列表qtypelist
輸出:試題列表questionlist
1.fori=0 in arrlist.Count-1 step 2
2.bookmark ←arrlist.Item(i)
3.bookmark.Range.Select() /*選擇段落*/
4.bookmark.Range.Copy() /*將段落放入剪切板*/
5.question ←questionlist.Item(i+1)
6.spnum←CreateA() /*生成題干文件*/
7.bookmarkB←arrlist.Item(i+1)
8.bookmarkB.Range.Select()
9.bookmarkB.Range.Copy()
10.CreateB() /*生成答案文件*/
/*保存到臨時(shí)數(shù)據(jù)庫*/
11.SaveFile(bookmark.Range.Text,bookmarkB.Range.Text,spnum,timestap)
12.endfor
為了驗(yàn)證本文所提方法的效率,對(duì)比了人工單題錄入與該方法的時(shí)間效率。實(shí)驗(yàn)數(shù)據(jù)從《數(shù)據(jù)庫原理與應(yīng)用》、《數(shù)據(jù)結(jié)構(gòu)》等5門課程中挑選,每門課程由三種不同題型的試題組成一套試卷,試卷詳細(xì)構(gòu)成見表1。
表1 試卷試題構(gòu)成統(tǒng)計(jì)表
實(shí)驗(yàn)過程由10人分別使用單題錄入和批量導(dǎo)入方法對(duì)所有試題集執(zhí)行試題錄入操作,并記錄單題錄入或批量導(dǎo)入的所用時(shí)間,最后計(jì)算每組試題的平均錄入和批量導(dǎo)入時(shí)間。實(shí)驗(yàn)結(jié)果如圖3所示。
圖3 耗時(shí)統(tǒng)計(jì)
從圖3可以看出,批量導(dǎo)入方法與單題錄入法相比,相同題量的試題,批量導(dǎo)入方法的時(shí)間效率明顯好于單題錄入方法。并且,隨著試題量的增加,單題錄入的時(shí)間消耗大致隨試題量成比例增加,而批量導(dǎo)入方法消耗的時(shí)間變化并不大??梢姡瑢?duì)于大批量的試題錄入,批量導(dǎo)入方法的優(yōu)勢(shì)更加顯著。
試題庫管理系統(tǒng)是否有具有高效實(shí)用的試題錄入方式是影響試題庫建設(shè)的關(guān)鍵因素。本文闡述了使用服務(wù)器端自動(dòng)化技術(shù)的試題智能提取與批量導(dǎo)入方法,在導(dǎo)入文檔模板設(shè)計(jì)上,制定了簡(jiǎn)單明了的標(biāo)識(shí)規(guī)則,只需對(duì)原有試卷稍做整理即可滿足導(dǎo)入要求,具有操作簡(jiǎn)單的優(yōu)點(diǎn)。在對(duì)文檔中的試題進(jìn)行自動(dòng)識(shí)別時(shí)分兩步完成,首先識(shí)別出各題型區(qū)域,再識(shí)別各題型區(qū)域內(nèi)的試題及答案,并為試題及答案標(biāo)注書簽,通過標(biāo)注的書簽自動(dòng)分割出試題及答案,最后將獲取的試題信息存入數(shù)據(jù)庫,該方法確保導(dǎo)入后的試題不會(huì)丟失任何信息。為了最大化減少人工干預(yù),提高試題質(zhì)量,在批量導(dǎo)入過程中還實(shí)現(xiàn)了填空題空數(shù)自動(dòng)識(shí)別和試題重復(fù)檢測(cè)。進(jìn)一步的實(shí)驗(yàn)表明,與人工單題錄入方式相比,本文提出的方法更加高效、便捷。目前該方法已集成到試題庫管理系統(tǒng)中,在實(shí)際應(yīng)用過程中成為用戶首選的試題錄入方法,具有良好的應(yīng)用效果。