與大多數(shù)程序員一樣,我經(jīng)常需要標(biāo)識(shí)存在于文本文檔中的部件和結(jié)構(gòu),這些文檔包括:日志文件、配置文件、分隔的數(shù)據(jù)以及格式更自由的(但還是半結(jié)構(gòu)化的)報(bào)表格式。所有這些文檔都擁有它們自己的“小語(yǔ)言”,用于規(guī)定什么能夠出現(xiàn)在文檔內(nèi)。
我編寫處理這些非正式解析任務(wù)的程序的方法總是有點(diǎn)象大雜燴,其中包括定制狀態(tài)機(jī)、正則表達(dá)式以及上下文驅(qū)動(dòng)的字符串測(cè)試。這些程序中的模式大概總是這樣:“讀一些文本,弄清是否可以用它來(lái)做些什么,然后可能再多讀一些文本,一直嘗試下去。”
各種形式的解析器將文檔中部件和結(jié)構(gòu)的描述提煉成簡(jiǎn)明、清晰和 說(shuō)明性的規(guī)則,該規(guī)則規(guī)定了如何標(biāo)識(shí)文檔的組成部分。這里,說(shuō)明性方面是最引人注目的。我所有的舊的特別的解析器都采用了這種風(fēng)格:讀一些字符、作決定、累加一些變量、清空、重復(fù)。正如本專欄關(guān)于函數(shù)型編程的部分文章中所評(píng)述的,程序流的方法風(fēng)格相對(duì)來(lái)說(shuō)容易出錯(cuò)并且難以維護(hù)。
正式解析器幾乎總是使用擴(kuò)展巴科斯范式(Extended Backus-Naur Form(EBNF))上的變體來(lái)描述它們所描述語(yǔ)言的“語(yǔ)法”。我們?cè)谶@里研究的工具是這樣做的,流行的編譯器開發(fā)工具 YACC(及其變體)也是這樣做的。基本上,EBNF 語(yǔ)法對(duì)您可能在文檔中找到的 部件賦予名稱;另外,經(jīng)常將較小的部件組成較大的部件。由運(yùn)算符 ― 通常和您在正則表達(dá)式中看到的符號(hào)相同 ― 來(lái)指定小部件在較大的部件中出現(xiàn)的頻率和順序。在解析器交談(parser-talk)中,語(yǔ)法中每個(gè)命名的部件稱為一個(gè)“產(chǎn)品(production)”。
可能讀者甚至還不知道 EBNF,卻已經(jīng)看到過(guò)運(yùn)行的 EBNF 描述了。例如,大家熟悉的 Python 語(yǔ)言參考大全(Python Language Reference)定義了浮點(diǎn)數(shù)在 Python 中是什么樣子:
EBNF 樣式的浮點(diǎn)數(shù)描述
floatnumber: pointfloat | exponentfloat
pointfloat: [intpart] fraction | intpart "."
exponentfloat: (nonzerodigit digit* | pointfloat) exponent
intpart: nonzerodigit digit* | "0"
fraction: "." digit+
exponent: ("e"|"E") ["+"|"-"] digit+
或者您可能見(jiàn)過(guò)以 EBNF 樣式定義的 XML DTD 元素。例如,developerWorks 教程的 <body> 類似于:
developerWorks DTD 中 EBNF 樣式的描述
代碼如下:<!ELEMENT body ((example-column | image-column)?, text-column) >
拼寫稍有不同,但是量化、交替和定序這些一般概念都存在于所有 EBNF 樣式的語(yǔ)言語(yǔ)法中。
使用 SimpleParse 構(gòu)建標(biāo)記列表
SimpleParse 是一個(gè)有趣的工具。要使用這個(gè)模塊,您需要底層模塊 mxTextTools ,它用 C 實(shí)現(xiàn)了一個(gè)“標(biāo)記引擎”。 mxTextTools (請(qǐng)參閱本文后面的 參考資料)的功能強(qiáng)大,但是相當(dāng)難用。一旦在 mxTextTools 上放置了 SimpleParse 后,工作就簡(jiǎn)單多了。
新聞熱點(diǎn)
疑難解答
圖片精選