概述
《漢文博士》允許使用者自己編寫詞典檔案。本文簡要講述了詞典編譯過程和相關配置檔案的編寫方法。
讀者需具備XML和正規表示式的基礎知識。
詞典編譯器
《漢文博士》的詞典編譯器可在“檔案”選單中點選“詞典編譯器”調出。
編譯前,需點選“載入”按鈕指定配置檔案。選定配置檔案後,將自動填寫輸出位置。點選編譯按鈕後,將編譯詞典,並自動註冊到程式的詞典庫,供後續檢索。
編譯配置檔案概述
詞典編譯配置檔案是一個XML檔案,程式使用配置檔案中定義的正規表示式處理指定的文字檔案,將文字內容轉換詞典內容。
提示:程式下載空間的詞典原始檔目錄下,有大量的示例可供參考。
編譯配置檔案至少應包含如下幾類資訊:
- 詞典的基礎資訊,如名稱(title)、版本(version)、出版社(publisher)、製作人(creator)、簡單說明(description)等
- 詞典的欄位(fields),例如:讀音、出處、解釋、例句等,全部都可自由定義。
- 一個詞典資料庫至少要有一個欄位(field)。
- 欄位需要指定名稱(name)和說明(description)。
- 預設的欄位資料型別(valueType)是純文字。
- 標記型別表示欄位內容為HTML。
- 此外還有其他型別,如拼音、粵拼、相關漢字列表等等。
- 欄位的內容,在顯示成檢索結果前,可以指定正規表示式模式(pattern),將欄位文字替換(replace)成其它內容(replacement,例如HTML標籤、跳轉連線等)。因此,不需要在原始檔裡插入大量的HTML標籤,在執行的時候可以轉換,從而使詞典能壓縮得再小一些。
- 詞典的資料(data)原始檔。
- 詞典的原始檔(file)是文字檔案,編譯器逐行(row)讀取文字檔案,根據正規表示式(pattern),提取出詞條(word),將其它文字則對映到相應的欄位(field)。
- 一部詞典可以有不止一個原始檔。
- 詞條可以重複。檢索詞條時,程式會將詞典中匹配該詞條的所有內容,按原始檔中的出現順序,依次顯示出來。
- 原始檔(encoding)建議使用UTF-8或GB18030編碼,以最大限度地支援可以顯示的漢字。
- 在編譯寫入詞典檔案前時,可以對文字作替換(replace)。
- 一般不需要考慮詞頭的簡繁體表示形式,程式可以在檢索時自動簡繁或異體通查。
- 一般也不需要考慮釋義正文的簡繁體表示形式,使用者可以選中詞典正文,在右鍵選單中將內容轉換成簡體或繁體。
除了上面必備的資訊之外,編譯配置檔案還支援如下高階特性:
- 詞典文件(documentation)。“文件”是指不屬於詞典詞條的輔助性內容,例如序、前言、凡例、附錄等等,使用者點選檢索結果的詞典標題,就會轉到詞典的基本資訊頁,在該頁面可檢視詞典的“文件”。詞典文件可以是文字檔案、HTML網頁或簡易Markdown檔案。
- 詞典資源(resource)。“資源”是指不被此條件檢索得到的資料內容,比較典型的就是插圖,此外,對於不希望直接在詞典文件列表中出現的內容,也可以放在資源內。
- 欄位分類(category)。欄位可以具有類別,在檢索結果中顯示。這個比較罕見,一般出現於結構比較複雜的詞典。
- 字典。由於字典為單字,檢索速度比詞典快一些。如果要編輯的詞典實際上是字典,或者包含大量單字,可以在配置檔案中指定字典屬性(enableCharacterDictionary),讓編譯出來的詞典中使用字典結構來儲存單字條目。
- 同義詞(synonym)。編譯器允許在配置檔案中使用正規表示式為詞頭指定同義詞。同義詞和詞頭的檢索結構都指向相同的釋義內容。在詞頭行(word)或內容行(row)中都可以指定同義詞。
編譯示例
下文以《本草害利》為例,講解詞典的編譯製作方法。原始檔可在程式下載空間的詞典資料庫→詞典原始檔→本草害利目錄下載。為方便起見,原始檔被壓縮成一個7zip檔案包,下載後請將檔案解壓出來,然後在編譯器中選擇其中的“本草害利.xml”進行編譯。
分析源文件結構
《本草害利》是清代凌奐編著的一部本草醫書,書中收錄了約三百種中藥材,作者基於用藥如用兵的思路,按臟腑和效能歸類,劃分成類似“心部藥隊〔補心猛將〕”、“肺部藥隊〔溫肺猛將〕”等分類,並逐一講解各藥材的“害”、“利”和“修治”。在本示例中,我們將藥材名稱作為詞頭。例如中藥材“五味子”的檢索結果如下圖所示:
原始檔開頭的部分內容大致如下圖所示(未換行,已隱藏長行後面的文字)。
編譯前的文件準備工作
為了編譯成詞典,我們對原始檔案作過一些編輯。例如:
- 在藥材名稱前加了一個“★”號,在編譯時,我們將指定“★”號後面的內容為詞頭。這種約定符號可以根據個人喜好而定,並沒有特殊要求。
- 由於藥材通常有別名,例如“北五味”又名“五味子”。原始檔案在“北五味”後本來沒有“、五味子”,如果不指定別名,輸入“五味子”的時候就查不到這個條目的內容了,因此,我們在“★北五味”後面,增加“、五味子”,並指定“、”為同義詞分隔符(synonymDelimiter)。
- 作者在講解一味藥材的時候,有時會提到其它藥材。我們希望,在檢索結果介面點選這些被提到的藥材,可以跳轉到對應的詞條。例如上圖的“乾薑”也是一味藥材,我們使用半形的中括號把它括起來,變成“[乾薑]”,表示可以點選的詞條連線。這種標記方式也是按個人喜好,我們在配置檔案中指定即可。
- 文中有些地方使用了藥材的簡稱,例如“(白芍)同歸地補血”,這裡“歸”指當歸,“地”指“地黃”,我們使用中括號加括號的方式表示這種關係,變成“同[歸](當歸)[地](地黃)補血”,在檢索結果介面中顯示的是“同歸地補血”,而點選“歸”字將檢索“當歸”,點選“地”字將檢索“地黃”。
再觀察一下檔案,由於藥材部隊出現在藥材名稱之前,也就是詞條標記“★”之前,因此,出現在“★北五味”前面的“心部藥隊〔補心猛將〕”將無法作為釋義寫入“北五味”詞條,而出現在“★酸棗仁”前的“心部藥隊〔補心猛將〕”,將成為“北五味”的內容。雖然在這裡沒關係,因為內容是一樣的,“北五味”也缺少藥材部隊的內容,但到後面切換藥材部隊的時候,就會錯位,造成內容錯誤。為了補救,我們將根據“藥隊〔”先忽略掉這裡出現的藥材部隊,另外再使用一個名叫“BowPad”的文字編輯器,透過正規表示式,提取出藥材部隊和藥材的關係,製作了一個名為“本草害利目錄.txt”的檔案,內容大致如下所示,每行一種藥材和藥材部隊的關係,中間用製表符分隔。編譯時,我們將這個檔案的內容放在前面先編譯,而正文檔案的內容放在後面編譯,就能得到開頭檢索結果的效果。
北五味、五味子 心部藥隊〔補心猛將〕
酸棗仁 心部藥隊〔補心猛將〕
柏子仁 心部藥隊〔補心猛將〕
遠志肉、遠志 心部藥隊〔補心猛將〕
丹參 心部藥隊〔補心猛將〕
編譯配置檔案
下面是編譯配置檔案:
<?xml version="1.0" encoding="gbk"?> <config title="本草害利" author="清·凌奐"> <fields> <field name="category" cssClass="categoryTitle" description="部隊" fieldNameVisible="no" mergeAdjacentField="true" adjacentFieldDelimiter="、"/> <field name="content" description="內容" valueType="標記" fieldNameVisible="no" fullText="yes"> <replace pattern="\[([^\]]+)\]\(([^\)]+)\)"> <replacement><![CDATA[<ref q="$2" title="$2">$1</ref>]]></replacement> </replace> <replace pattern="\[([^\]]+)\]" replacement="<ref>$1</ref>"/> <replace pattern="〔.+〕" replacement="<b>$0</b>"/> </field> </fields> <data file="本草害利目錄.txt" encoding="GBK" synonymDelimiter="、"> <column field="1"/> </data> <data file="本草害利.txt" encoding="utf-8" synonymDelimiter="、" allowEmptyLine="true"> <word pattern="^★" removePattern="true"/> <row field="2"> <ignore pattern="藥隊〔" useRegex="false"/> <ignore pattern="(見" useRegex="false" /> <ignore pattern="(同上" useRegex="false" /> </row> </data> </config>
“config”元素中出現的“title”和“author”屬性分別表示詞典的名稱和作者。這些將會顯示在檢索結果視窗上。
詞典欄位
“fields”元素下的“field”元素指定了兩個欄位。第一個欄位將用來顯示藥材部隊,第二個欄位用於顯示“害”、“利”、“修治”等正文。
第一個欄位的程式碼如下:
<field name="category" cssClass="categoryTitle" description="部隊" fieldNameVisible="no" mergeAdjacentField="true" adjacentFieldDelimiter="、"/>
從檢索結果視窗可以明顯看出,兩個欄位的顯示效果是不一樣的。原因是第一個“name="category"”的欄位具有“cssClass="categoryTitle"”屬性,表示使用類名為“categoryTitle”CSS樣式。可以使用的樣式,是在程式目錄的style.css檔案中定義的。程式預設會將欄位描述名稱(這裡是“部隊”)顯示到檢索結果,指定“fieldNameVisible="no"”可以隱藏欄位名稱。
由於一種藥材可能同屬於多個藥材部隊,例如“北五味、五味子”在上面的目錄檔案中一共出現了三次,因此,需要程式把它們串起來。我們可在配置中指定“mergeAdjacentField="true"”將相鄰的相同欄位結果串連起來,另外再指定“adjacentFieldDelimiter="、"”,使用逗號將內容分隔開。這樣,就得到了開頭截圖裡幾個藥材部隊串連在一起的效果了。
第二個欄位的內容較多,程式碼如下:
<field name="content" description="內容" valueType="標記" fieldNameVisible="no" fullText="yes"> <replace pattern="\[([^\]]+)\]\(([^\)]+)\)"> <replacement><![CDATA[<ref q="$2" title="$2">$1</ref>]]></replacement> </replace> <replace pattern="\[([^\]]+)\]" replacement="<ref>$1</ref>"/> <replace pattern="〔.+〕" replacement="<b>$0</b>"/> </field>
“valueType="標記"”屬性表示這個欄位的內容是HTML標記(如果不加這個屬性,所有內容就會被視為純文字,也就不會有跳轉連線)。
“replace”元素指定了在顯示檢索結果之前對欄位內容的替換操作。“pattern”屬性的值是正規表示式,用於匹配文字,而“replacement”屬性或元素的內容則表示替代的內容。其中“$1”、“$2”之類的標記表示正規表示式捕獲的分組內容。
第一組替換表示式“\[([^\]]+)\]\(([^\)]+)\)”表示替換類似於前文提到的“同[歸](當歸)[地](地黃)補血”模式。“[歸](當歸)”將被替換成“<ref q="當歸" title="當歸">歸</ref>”。“ref”元素在程式中有特別的含義,表示詞條連線。其中“q”屬性表示要檢索的詞條,“title”屬性就是滑鼠移上連線所顯示的提示文字,“ref”元素的文字就是連線的內容。由於本組替換內容比較複雜,因此我們把它放在一個CDATA節中,避免為替換目標的引號和尖括號編寫不直觀的實體引用。
第二組替換表示式“\[([^\]]+)\]”表示替換類似於前文提到的“[乾薑]”的模式。“[乾薑]”將被替換成“<ref>乾薑</ref>”。由於要檢索的內容和要顯示的內容是一樣的,所以就不需要“q”屬性了。
在安排替換順序時,要注意先後,必須先替換掉方括號加圓括號的模式,再替換僅有方括號的模式,如果反過來,“[歸](當歸)”就會被“\[([^\]]+)\]”模式對應的替換組替換成“<ref>歸</ref>(當歸)”,而“\[([^\]]+)\]\(([^\)]+)\)”就得不到替換文字的機會了。
第三組替換表示式“〔.+〕”表示將“〔害〕”、“〔利〕”和“〔修治〕之類的內容替換成粗體。
資料來源檔案
欄位元素後出現的“data”元素代表詞典資料來源檔案。原始檔是文字檔案,一般有列布局和行佈局。
- 如果檔案使用列布局中,每行一條詞頭,使用製表符分列,第一列為詞頭,後面各列為欄位資料。
- 大部分檔案使用行佈局,程式逐行讀取文字,根據正規表示式確定每行資料對應詞頭還是欄位。
第一個原始檔對應的配置程式碼如下:
<data file="本草害利目錄.txt" encoding="GBK" synonymDelimiter="、"> <column field="1"/> </data>
“encoding”表示該文字檔案的文字編碼,要注意必須與“file”屬性對應檔案的編碼保持一致,否則就會無法讀取。
“synonymDelimiter="、"”指定了詞頭的同義詞分隔符。
注意“data”元素下出現的“column”子元素。這個元素表示這個文件是列布局(按製表符分列),其中第1列隱含表示詞頭,各個“column”元素表示後面的列,將對應欄位內容。因此,這個“column”元素對應第二列,對應的“field="1"”屬性表示該列對應前面定義的第1個“field”元素對應的欄位(即藥材部隊)內容。
在本示例中,檔案只有兩列(大致如下所示),第一列對應的是詞頭,在“synonymDelimiter="、"”的作用下,諸如“北五味、五味子”的詞頭被拆分成“北五味”和“五味子”兩個詞頭。第二列“心部藥隊〔補心猛將〕”對應藥材部隊欄位。
北五味、五味子 | 心部藥隊〔補心猛將〕 |
酸棗仁 | 心部藥隊〔補心猛將〕 |
柏子仁 | 心部藥隊〔補心猛將〕 |
第二個原始檔的程式碼如下:
<data file="本草害利.txt" encoding="utf-8" synonymDelimiter="、" allowEmptyLine="true"> <word pattern="^★" removePattern="true"/> <row field="2"> <ignore pattern="藥隊〔" useRegex="false"/> <ignore pattern="(見" useRegex="false" /> <ignore pattern="(同上" useRegex="false" /> </row> </data>
由於原始檔有大量的空行。按程式的預設設定,空行被作為詞條內容的結束標記,而本例項的檔案並非如此。因此,我們可以指定“allowEmptyLine="true"”,允許空行出現在詞條內容之中。編譯器在編譯時就會自動將空行忽略掉。
這裡出現的“word”元素表示詞頭,“row”元素表示除詞頭外用於識別各行對應哪個欄位的規則。
“word”元素的“pattern”屬性表示行首以五角星開頭的行被識別為詞頭。
由於五角星在這裡已經被用來識別詞頭了,而它實際上並不是詞條文字,只是我們為方便程式編譯器識別詞頭而加的標記,因此要把它刪掉。removePattern="true"屬性,就表示刪掉匹配“pattern”屬性的內容,這樣,行首的五角星就不會出現在詞頭了。
由於這個“data”元素也有一個“synonymDelimiter="、"”屬性,因此,頓號也被用來分隔詞頭。
“row”元素的“field="2"”屬性表示這個元素為第二個欄位,即前面定義的“內容”欄位定義規則。
由於文件中只有正文內容,沒有其它內容了,所以不需要篩選。如果一個文件裡有多個欄位,可以在“row”元素裡指定“pattern”屬性,使用正規表示式匹配欄位。
三個“ignore”元素在這裡表示忽略匹配“pattern”屬性內容的行,“useRegex="false"”屬性表示“pattern”屬性是普通文字,不是正規表示式。如果原始檔某一行文字的內容包含這裡三個“pattern”中的一個,都會被忽略。
在一個“row”元素內,也可以指定“replace”元素,在寫入詞典之前替換掉一些內容。
總結
本文講解了詞典編譯的工具和配置檔案的常見元素和屬性設定,並以《本草害利》為例講解了詞典編譯配置檔案的編寫方法。本文講解的編譯配置只是較基礎的部分,還有更多的功能將在後續的文章講解。
詞典原始檔的示例可在下載區下載 (訪問密碼: 8518)
從上述設計也可見,《漢文博士》的詞典編譯器支援多種文字檔案佈局,比較靈活。欄位的概念為建構詞典邏輯內容提供了方便。獨特的執行時文字替換機制,使得詞典原始檔可以做得比較簡潔,無需嵌入大量標籤,也減小了詞典檔案的空間佔用。而程式簡繁異體通查和獨有的同義詞機制便於詞條檢索,也使詞典制作人員可以更專注於創作優質內容,不會被詞典程式的功能限制所困擾。