亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

在Python中使用SimpleParse模塊進行解析的教程

系統 1822 0

與大多數程序員一樣,我經常需要標識存在于文本文檔中的部件和結構,這些文檔包括:日志文件、配置文件、分隔的數據以及格式更自由的(但還是半結構化的)報表格式。所有這些文檔都擁有它們自己的“小語言”,用于規定什么能夠出現在文檔內。

我編寫處理這些非正式解析任務的程序的方法總是有點象大雜燴,其中包括定制狀態機、正則表達式以及上下文驅動的字符串測試。這些程序中的模式大概總是這樣:“讀一些文本,弄清是否可以用它來做些什么,然后可能再多讀一些文本,一直嘗試下去。”

各種形式的解析器將文檔中部件和結構的描述提煉成簡明、清晰和 說明性的規則,該規則規定了如何標識文檔的組成部分。這里,說明性方面是最引人注目的。我所有的舊的特別的解析器都采用了這種風格:讀一些字符、作決定、累加一些變量、清空、重復。正如本專欄關于函數型編程的部分文章中所評述的,程序流的方法風格相對來說容易出錯并且難以維護。

正式解析器幾乎總是使用擴展巴科斯范式(Extended Backus-Naur Form(EBNF))上的變體來描述它們所描述語言的“語法”。我們在這里研究的工具是這樣做的,流行的編譯器開發工具 YACC(及其變體)也是這樣做的?;旧?,EBNF 語法對您可能在文檔中找到的 部件賦予名稱;另外,經常將較小的部件組成較大的部件。由運算符 ?D 通常和您在正則表達式中看到的符號相同 ?D 來指定小部件在較大的部件中出現的頻率和順序。在解析器交談(parser-talk)中,語法中每個命名的部件稱為一個“產品(production)”。

可能讀者甚至還不知道 EBNF,卻已經看到過運行的 EBNF 描述了。例如,大家熟悉的 Python 語言參考大全(Python Language Reference)定義了浮點數在 Python 中是什么樣子:
EBNF 樣式的浮點數描述

floatnumber:??? pointfloat | exponentfloat
pointfloat:???? [intpart] fraction | intpart "."
exponentfloat:? (nonzerodigit digit* | pointfloat) exponent
intpart:??????? nonzerodigit digit* | "0"
fraction:?????? "." digit+
exponent:?????? ("e"|"E") ["+"|"-"] digit+

或者您可能見過以 EBNF 樣式定義的 XML DTD 元素。例如,developerWorks 教程的 類似于:
developerWorks DTD 中 EBNF 樣式的描述

復制代碼 代碼如下:

拼寫稍有不同,但是量化、交替和定序這些一般概念都存在于所有 EBNF 樣式的語言語法中。
使用 SimpleParse 構建標記列表

SimpleParse 是一個有趣的工具。要使用這個模塊,您需要底層模塊 mxTextTools ,它用 C 實現了一個“標記引擎”。 mxTextTools (請參閱本文后面的 參考資料)的功能強大,但是相當難用。一旦在 mxTextTools 上放置了 SimpleParse 后,工作就簡單多了。

使用 SimpleParse 確實很簡單,因為不需要考慮 mxTextTools 的大部分復雜性。首先,應該創建一種 EBNF 樣式的語法,用來描述要處理的語言。第二步是調用 mxTextTools 來創建一個 標記列表,當語法應用于文檔時,該列表描述所有成功的產品。最后,使用 mxTextTools 返回的標記列表來進行實際操作。

對于本文,我們要解析的“語言”是“智能 ASCII”所使用的一組標記代碼,這些代碼用來表示諸如黑體、模塊名以及書籍標題之類的內容。這就是先前使用 mxTextTools 來標識的同一種語言,在先前的部分中,使用正則表達式和狀態機。該語言比完整的編程語言簡單得多,但已經足夠復雜而有代表性。

這里,我們可能需要回顧一下。 mxTextTools 提供給我們的“標記列表”是什么東西?這基本上是一個嵌套結構,它只是給出了每個產品在源文本中匹配的字符偏移量。 mxTextTools 快速遍歷源文本,但是它不對源文本本身 做任何操作(至少當使用 SimpleParse 語法時不進行任何操作)。讓我們研究一個簡化的標記列表:
從 SimpleParse 語法生成的標記列表

            
(1,
 [('plain',
  0,
  15,
  [('word', 0, 4, [('alphanums', 0, 4, [])]),
  ('whitespace', 4, 5, []),
  ('word', 5, 10, [('alphanums', 5, 10, [])]),
  ('whitespace', 10, 11, []),
  ('word', 11, 14, [('alphanums', 11, 14, [])]),
  ('whitespace', 14, 15, [])]),
 ('markup',
  15,
  27,
 ...
 289)


          

中間的省略號表示了一批更多的匹配。但是我們看到的部分敘述了下列內容。根產品(“para”)取得成功并結束于偏移量 289 處(源文本的長度)。子產品“plain”的偏移量為 0 到 15?!皃lain”子產品本身由更小的產品組成。在“plain”產品之后,“markup”產品的偏移量為 15 到 27。這里省略了詳細信息,但是第一個“markup”由組件組成,并且源文本中稍后還有另外的產品取得成功。

“智能 ASCII”的 EBNF 樣式的語法

我們已經瀏覽了 SimpleParse + mxTextTools 所能提供的標記列表。但是我們確實需要研究用來生成這個標記列表的語法。實際工作在語法中發生。EBNF 語法讀起來幾乎不需加以說明(盡管 確實需要一點思考和測試來設計一個語法):
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 / title
emph      := '-', plain, '-'
strong     := '*', plain, '*'
module     := '[', plain, ']'
code      := "'", plain, "'"
title     := '_', plain, '_'
punctuation  := (safepunct / mdash)
mdash     := '--'
safepunct   := [!@#$%^&()+=|\{}:;<>,.?/"]


          

這種語法和您口頭描述“智能 ASCII”的方式幾乎完全相同,非常清晰。段落由一些純文本和一些標記文本組成。純文本由某些字、空白和標點符號的集合組成。標記文本可能是強調文本、著重強調文本或模塊名等等。著重強調文本由星號環繞。標記文本就是由諸如此類的部分組成的。需要考慮的是幾個特性,類似于到底什么是“字”,或者可以用什么符號結束縮寫,但是 EBNF 的句法不會成為障礙。

相比之下,使用正則表達式可以更精練地描述同類規則?!爸悄?ASCII”標記程序的第一個版本就是這樣做的。但是編寫這種精練難度大得多,并且以后調整也更為困難。下列代碼表示了很大程度上(但不精確地)相同的規則集:
智能 ASCII 的 Python regexs

            
# [module] names
    
re_mods =  
    r""'([\(\s'/">]|^)\[(.*?)\]([<\s\.\),:;'"?!/-])"""
# *strongly emphasize* words
    
re_strong = 
    r""'([\(\s'/"]|^)\*(.*?)\*([\s\.\),:;'"?!/-])"""
# -emphasize- words
    
re_emph =  
    r""'([\(\s'/"]|^)-(.*?)-([\s\.\),:;'"?!/])"""
# _Book Title_ citations
    
re_title = 
    r""'([\(\s'/"]|^)_(.*?)_([\s\.\),:;'"?!/-])"""
# 'Function()" names
    
re_funcs = 
    r""'([\(\s/"]|^)'(.*?)'([\s\.\),:;"?!/-])"""


          

如果您發現或發明了該語言的某種經過微小更新的變體,將它和 EBNF 語法一起使用要比和那些正則表達式一起使用簡單得多。此外,通常使用 mxTextTools 執行模式操作甚至更快些。

生成和使用標記列表

對于樣本程序,我們將實際語法放置在一個單獨的文件中。對于大多數用途而言,這種組織比較好,便于使用。通常,更改語法和更改應用程序邏輯是不同種類的任務;這些文件反映了這一點。但是我們對語法所做的全部處理就是將它作為一個字符串傳遞給 SimpleParse 函數,因此我們大體上可以將它包括到主應用程序中(或者甚至以某種方式動態生成它)。

讓我們研究完整的(簡化)標記應用程序:
typographify.py

            
import
     os
    from
     sys 
    import
     stdin, stdout, stderr
    from
     simpleparse 
    import
     generator
    from
     mx.TextTools 
    import
     TextTools
input = stdin.read()
decl = open(
    'typographify.def'
    ).read()
    from
     typo_html 
    import
     codes
parser = generator.buildParser(decl).parserbyname(
    'para'
    )
taglist = TextTools.tag(input, parser)
    for
     tag, beg, end, parts 
    in
     taglist[1]:
  
    if
     tag == 
    'plain'
    :
    stdout.write(input[beg:end])
  
    elif
     tag == 
    'markup'
    :
    markup = parts[0]
    mtag, mbeg, mend = markup[:3]
    start, stop = codes.get(mtag, (
    '
                        '
    ,
    '
                        '
    ))
    stdout.write(start + input[mbeg+1:mend-1] + stop)
stderr.write(
    'parsed %s chars of %s\n'
     % (taglist[-1], len(input)))


          

這就是它所做的。首先讀入語法,然后根據語法創建一個 mxTextTools 解析器。接下來,我們將標記表/解析器應用于輸入源來創建一個標記列表。最后,我們循環遍歷標記列表,并且發出一些新的標記文本。當然,該循環可以對遇到的每個產品做我們所期望的任何其它事情。

由于智能 ASCII 所使用的特殊語法,源文本中的任何內容都可歸類于“plain”產品或“markup”產品。因此,對于循環遍歷標記列表中的單個級別,它已經足夠了(除非我們正好尋找比特定標記產品級別低一級的級別,譬如“title”)。但是格式更自由的語法 ?D 譬如出現在大多數編程語言中的語法 ?D 可以輕松地在標記列表中向下遞歸,并在每個級別上尋找產品名稱。例如,如果一種語法中允許嵌套標記代碼,或許可以使用這種遞歸風格。您可能會喜歡弄清如何調整語法的練習(提示:請記住允許各產品彼此遞歸)。

轉至輸出的特殊標記代碼還是存儲到另一個文件中了,這是由于組織的原因而非本質原因。在這里我們使用了一個技巧,就是用一個字典作為一個 switch 語句(盡管示例中的 otherwise 情況還是太狹窄了)。這個想法就是:將來我們可能希望創建多種“輸出格式”的文件,比如說 HTML、DocBook、LaTeX 或者其它格式。用于示例的特殊標記文件類似于:
typo_html.py

            
codes = \
{ 
    'emph'
      : (
    '
            
              '
    , 
    '
            
            '
    ),
 
    'strong'
     : (
    '
            
              '
    , 
    '
            
            '
    ),
 
    'module'
     : (
    '
            
              
                '
    , 
    '
              
            
            '
    ),
 
    'code'
      : (
    '
            
              '
    , 
    '
            
            '
    ),
 
    'title'
      : (
    '
            
              '
    , 
    '
            
            '
    ),
}


          

把這種格式擴展到其它輸出格式很簡單。

結束語

SimpleParse 為含義模糊的 mxTextTools C 模塊的基本功能和速度提供了一種簡明的并且十分易讀的 EBNF 樣式的封裝器。此外,即使只是順便學會的,許多程序員也已經相當熟悉 EBNF 語法了。關于什么更容易理解,我不能提供 證明 ?D 這一點因各人的直覺而異 ?D 但是我可以根據源代碼長度給出量化評估。先前手工開發的 mxTypographify 模塊的大小如下:

復制代碼 代碼如下:
wc mxTypographify.py

199???? 776??? 7041 mxTypographify.py

這 199 行中,相當數量的行是注釋。這些行中有 18 行是標記函數所包含的正則表達式版本,包含該標記函數是用于計時比較。但是該程序的功能基本上和上面列出的 typographify.py 的功能相同。相比之下,我們的 SimpleParse 程序,包括其支持文件在內,大小如下:

復制代碼 代碼如下:
wc typo*.def typo*.py

19????? 79???? 645 typographify.def
20????? 79???? 721 typographify.py
?6????? 25???? 205 typo_html.py
45???? 183??? 1571 total

換句話說,行數大約只有前者的四分之一。這個版本的注釋較少,但是那主要是因為 EBNF 語法的自我描述能力很強。我不希望太過強調代碼行數 ?D 顯然,您可以通過最小化或最大化代碼長度做手腳。但是通常對程序員的工作進行研究,少數實際經驗結論之一是:“千行代碼/人月”相當接近于常數,和語言以及庫關系不大。當然,依次地,正則表達式版本是 SimpleParse 版本長度的三分之一 ?D 但是我認為其表達式的密度使得它極難維護并且更難編寫。總而言之,我認為 SimpleParse 是所考慮的方法中最好的。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久的爱久久的你 | 亚洲精品久久久久网站 | 91麻豆精品| 欧美第3页 | 日本人一级毛片免费视频 | 奇米影视777欧美在线观看 | 免费一级欧美片在线观看 | 成人9久久国产精品品 | 国产免费一级精品视频 | 成人18免费网站 | 99热久久国产精品这里小说 | 国产精品久久久久久久hd | 久久精品综合免费观看 | 国产成人h片视频在线观看 国产成人h综合亚洲欧美在线 | 亚洲天堂一区二区 | 91在线精品亚洲一区二区 | 日韩特级毛片免费观看视频 | 亚洲热线99精品视频 | 在线播放a 1 | 97午夜理伦片在线影院 | 亚洲欧美一区二区三区在饯 | 久久精品国产一区二区三区日韩 | 亚洲精品 国产 日韩 | 久久影院国产 | 亚洲网址在线观看 | 日韩高清成人 | 欧美成人午夜精品一区二区 | 亚洲福利社 | 久久午夜宅男免费网站 | 日韩在线色 | 奇米第四色在线视频 | 国产成人精品视频免费 | 高清视频 一区二区三区四区 | 久久久亚洲欧洲国产 | 久久精品国产四虎 | 免费看成人播放毛片 | 97精品久久久久中文字幕 | 性夜黄a爽爽免费视频国产 性夜影院爽黄a爽免费看网站 | 韩国欧美一级毛片免费 | 天天插日日操 | 亚洲国产精品一区二区久 |