在日常的編程中,我經(jīng)常需要標(biāo)識(shí)存在于文本文檔中的部件和結(jié)構(gòu),這些文檔包括:日志文件、配置文件、定界的數(shù)據(jù)以及格式更自由的(但還是半結(jié)構(gòu)化的)報(bào)表格式。所有這些文檔都擁有它們自己的“小語言”,用于規(guī)定什么能夠出現(xiàn)在文檔內(nèi)。我編寫這些非正式解析任務(wù)的程序的方法總是有點(diǎn)象大雜燴,其中包括定制狀態(tài)機(jī)、正則表達(dá)式以及上下文驅(qū)動(dòng)的字符串測(cè)試。這些程序中的模式大概總是這樣:“讀一些文本,弄清是否可以用它來做些什么,然后可能再多讀一些文本,一直嘗試下去。”
解析器將文檔中部件和結(jié)構(gòu)的描述提煉成簡明、清晰和 說明性的規(guī)則,確定由什么組成文檔。大多數(shù)正式的解析器都使用擴(kuò)展巴科斯范式(Extended Backus-Naur Form,EBNF)上的變體來描述它們所描述的語言的“語法”。基本上,EBNF 語法對(duì)您可能在文檔中找到的 部件賦予名稱;另外,較大的部件通常由較小的部件組成。小部件在較大的部件中出現(xiàn)的頻率和順序由操作符指定。舉例來說,清單 1 是 EBNF 語法 typographify.def,我們?cè)?SimpleParse 那篇文章中見到過這個(gè)語法(其它工具運(yùn)行的方式稍有不同):
清單 1. typographify.def
para := (plain / markup)+plain := (word / whitespace / punctuation)+whitespace := [ /t/r/n]+alphanums := [a-zA-Z0-9]+word := alphanums, (wordpunct, alphanums)*, contraction?wordpunct := [-_]contraction := "'", ('am'/'clock'/'d'/'ll'/'m'/'re'/'s'/'t'/'ve')markup := emph / strong / module / code / titleemph := '-', plain, '-'strong := '*', plain, '*'module := '[', plain, ']'code := "'", plain, "'"title := '_', plain, '_'punctuation := (safepunct / mdash)mdash := '--'safepunct := [!@#$%^&()+=|/{}:;<>,.?/"]
Spark 簡介
Spark 解析器與 EBNF 語法有一些共同之處,但它將解析/處理過程分成了比傳統(tǒng)的 EBNF 語法所允許的更小的組件。Spark 的優(yōu)點(diǎn)在于,它對(duì)整個(gè)過程中每一步操作的控制都進(jìn)行了微調(diào),還提供了將定制代碼插入到過程中的能力。您如果讀過本系列的 SimpleParse 那篇文章,您就會(huì)回想起我們的過程是比較粗略的:1)從語法(并從源文件)生成完整的標(biāo)記列表,2)使用標(biāo)記列表作為定制編程操作的數(shù)據(jù)。
Spark 與標(biāo)準(zhǔn)的基于 EBNF 的工具相比缺點(diǎn)在于,它比較冗長,而且缺少直接的出現(xiàn)計(jì)量符(即表示存在的“+”,表示可能性的“*”和表示有限制性的“?”)。計(jì)量符可以在 Spark 記號(hào)賦予器(tokenizer)的正則表達(dá)式中使用,并可以用解析表達(dá)式語法中的遞歸來進(jìn)行模擬。如果 Spark 允許在語法表達(dá)式中使用計(jì)量,那就更好了。另一個(gè)值得一提的缺點(diǎn)是,Spark 的速度與 SimpleParse 使用的基于 C 的底層 mxTextTools 引擎相比遜色很多。
|
新聞熱點(diǎn)
疑難解答
圖片精選