跳轉到內容

通用 Lisp/外部庫/CL-PPCRE

來自華夏公益教科書,開放的書籍,開放的世界

Common Lisp 可移植 Perl 相容正則表示式庫,或 CL-PPCRE,將 Perl 正則表示式的強大功能帶到 Common Lisp。用作者 Edi Weitz 的話說,CL-PPCRE 具有以下特點

  • 它與 Perl 相容。
  • 它速度很快。
  • 它可以在符合 ANSI 標準的 Common Lisp 實現之間移植。
  • 它是執行緒安全的。
  • 除了像 Perl 一樣將正則表示式指定為字串之外,您還可以使用 S 表示式。
  • 它附帶 BSD 風格的許可證,因此您可以基本上隨心所欲地使用它。

基本用法

[編輯 | 編輯原始碼]

CL-PPCRE 的主要入口點是scan函式。Scan接受一個正則表示式(或 regex)和一個要匹配的字串,並返回 regex 的匹配開始和結束索引,以及您在 regex 中定義的任何暫存器的開始和結束索引。

CL-USER> (scan "b(.)r" "foo bar baz bur")
4
7
#(5)
#(6)

第一個和第二個返回值分別是匹配子字串的開始和結束。第三和第四個返回值標記暫存器匹配的開始和結束索引。注意,它只找到了第一個例項 bar。要找到下一個例項,您可以為關鍵字引數傳遞值:start. 不用說,您也可以透過指定 :end 關鍵字來限制掃描在字串上執行的距離。

;; Match the next instance of "b.r" at or after position 5
CL-USER> (scan "b.r" "foo bar baz bur" :start 5)
12
15
#()
#()

;; This fails, because there is no match between 5 and 13 
;; (even though the regex has started at 12, it doesn't 
;; finish by 13)
CL-USER> (scan "b.r" "foo bar baz bur" :start 5 :end 13)
NIL

您可能已經注意到,在掃描字串時跟蹤起點可能有點繁瑣。為此,CL-PPCRE 為常見任務提供了一些方便的函式和宏,例如

  • do-scans
  • scan-to-strings
  • do-matches
  • do-register-groups
  • all-matches
  • split
  • regex-replace
  • regex-replace-all

正則表示式作為樹和閉包

[編輯 | 編輯原始碼]

雖然 CL-PPCRE 將正則表示式作為字串,但實際上它會將該字串解析為正則表示式樹。除了是 Lisp 的做法之外,這也消除了正則表示式的許多神秘之處,但是,它用冗長性來交換它。一個很好的特性是,由於您手頭有正則表示式的解析樹,因此可以直觀地以程式設計方式動態構建或修改正則表示式。

;;; The unexported function cl-ppcre::parse-string parses a 
;;; regex string into a regex tree.  This allows us to see
;;; how strings translate to trees.

;; Character alternatives, Classes, wildcards
CL-USER> (cl-ppcre::parse-string "[abcdef][^abcdef]")
(:SEQUENCE (:CHAR-CLASS #\a #\b #\c #\d #\e #\f)
 (:INVERTED-CHAR-CLASS #\a #\b #\c #\d #\e #\f))
;; Note the double backslashes standard Lisp technique to 
;; get a literal backslash
CL-USER> (cl-ppcre::parse-string ".\\d\\D\\w\\W\\s\\S")
(:SEQUENCE :EVERYTHING :DIGIT-CLASS :NON-DIGIT-CLASS :WORD-CHAR-CLASS 
 :NON-WORD-CHAR-CLASS :WHITESPACE-CHAR-CLASS :NON-WHITESPACE-CHAR-CLASS)

;; Repetitions (Note that "a*?" doesn't make much sense as 
;; it will always match the empty string, but it does this
;; correctly)
CL-USER> (cl-ppcre::parse-string "Greedy:a*b+c{2,5}d{2,}Non-greedy:a*?b+?c{2,5}?d{2,}?")
(:SEQUENCE
 "Greedy:"
 (:GREEDY-REPETITION 0 NIL #\a)
 (:GREEDY-REPETITION 1 NIL #\b)
 (:GREEDY-REPETITION 2 5 #\c)
 (:GREEDY-REPETITION 2 NIL #\d)
 "Non-greedy:"
 (:NON-GREEDY-REPETITION 0 NIL #\a)
 (:NON-GREEDY-REPETITION 1 NIL #\b)
 (:NON-GREEDY-REPETITION 2 5 #\c)
 (:NON-GREEDY-REPETITION 2 NIL #\d))

;; Alternatives
CL-USER> (cl-ppcre::parse-string "a|b|c")
(:ALTERNATION #\a #\b #\c)

;; Groups, Registers, and Back References.
CL-USER> (cl-ppcre::parse-string "(a..)+(?:def)+\\1")
(:SEQUENCE
 (:GREEDY-REPETITION 1 NIL (:REGISTER (:SEQUENCE #\a :EVERYTHING :EVERYTHING)))
 (:GREEDY-REPETITION 1 NIL (:GROUP "def")) (:BACK-REFERENCE 1))
;; This matches strings like "abcdefdefabc"

在獲得正則表示式的樹形形式後,CL-PPCRE 使用函式create-scanner來編譯該表示形式(許多 Lisp 編譯為本地機器程式碼)。這做兩件事:(1)它往往使正則表示式掃描非常快,並且(2)它往往使第一次定義正則表示式變得很昂貴(由於編譯開銷,儘管有一些變數可以設定為減少這種開銷)。編譯完成後,您可以重複使用同一個表示式來規避編譯開銷。CL-PPCRE 還使用適當的演算法生成高效的正則表示式表示,即不是基於堆疊的系統。總的來說,這是一個非常高效的庫。

;; Create a scanner closure that finds matches that resemble IPs
;; and fill registers with the numbers
CL-USER> (cl-ppcre::create-scanner "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})")
#<CLOSURE (LAMBDA (STRING CL-PPCRE::START CL-PPCRE::END)) {12E62C3D}>
NIL

;; using * as last return value
CL-USER> (cl-ppcre::scan-to-strings * "192.168.1.255")
"192.168.1.255"
#("192" "168" "1" "255")

好吧,使用 CL-PPCRE 與使用任何其他正則表示式引擎並沒有太大區別。在這裡,我們將看到一些快速而骯髒的使用示例。如果您在如何執行某個操作時遇到困難,請查閱正則表示式教程,perlre手冊頁,或 Perl 使用者。您只需要記住將您的 regexs 雙反斜槓(就像您始終必須執行的操作一樣,將文字反斜槓插入 Lisp 字串中)。

掃描 HTML 標籤

[編輯 | 編輯原始碼]
CL-USER> (defparameter *url-regex*  "((([A-Za-z]{3,9}:(?://)?)(?:[-;:&=+$,\\w]+@)?[A-Za-z0-9.-]+|(?:www\\.|[-;:&=+$,\\w]+@)[A-Za-z0-9.-]+)((?:/[+~%/.\\w-]*)?\\??(?:[-+=&;%@.\\w]*)#?(?:[.!/\\w]*))?)")

CL-USER> (cl-ppcre::parse-string *url-regex*)
(:REGISTER                                                                                                                                                                                                                                                     
 (:SEQUENCE                                                                                                                                                                                                                                                    
  (:REGISTER                                                                                                                                                                                                                                                   
   (:ALTERNATION                                                                                                                                                                                                                                               
    (:SEQUENCE (:REGISTER (:SEQUENCE (:GREEDY-REPETITION 3 9 (:CHAR-CLASS (:RANGE #\A #\Z) (:RANGE #\a #\z))) #\: (:GREEDY-REPETITION 0 1 (:GROUP "//"))))                                                                                                     
     (:GREEDY-REPETITION 0 1 (:GROUP (:SEQUENCE (:GREEDY-REPETITION 1 NIL (:CHAR-CLASS #\- #\; #\: #\& #\= #\+ #\$ #\, :WORD-CHAR-CLASS)) #\@)))                                                                                                               
     (:GREEDY-REPETITION 1 NIL (:CHAR-CLASS (:RANGE #\A #\Z) (:RANGE #\a #\z) (:RANGE #\0 #\9) #\. #\-)))                                                                                                                                                      
    (:SEQUENCE (:GROUP (:ALTERNATION "www." (:SEQUENCE (:GREEDY-REPETITION 1 NIL (:CHAR-CLASS #\- #\; #\: #\& #\= #\+ #\$ #\, :WORD-CHAR-CLASS)) #\@)))                                                                                                        
     (:GREEDY-REPETITION 1 NIL (:CHAR-CLASS (:RANGE #\A #\Z) (:RANGE #\a #\z) (:RANGE #\0 #\9) #\. #\-)))))                                                                                                                                                    
  (:GREEDY-REPETITION 0 1                                                                                                                                                                                                                                      
   (:REGISTER                                                                                                                                                                                                                                                  
    (:SEQUENCE (:GREEDY-REPETITION 0 1 (:GROUP (:SEQUENCE #\/ (:GREEDY-REPETITION 0 NIL (:CHAR-CLASS #\+ #\~ #\% #\/ #\. :WORD-CHAR-CLASS #\-)))))                                                                                                             
     (:GREEDY-REPETITION 0 1 #\?) (:GROUP (:GREEDY-REPETITION 0 NIL (:CHAR-CLASS #\- #\+ #\= #\& #\; #\% #\@ #\. :WORD-CHAR-CLASS)))                                                                                                                           
     (:GREEDY-REPETITION 0 1 #\#) (:GROUP (:GREEDY-REPETITION 0 NIL (:CHAR-CLASS #\. #\! #\/ :WORD-CHAR-CLASS))))))))                                                                                                                                          
                                                                                                      
CL-USER> (cl-ppcre::scan-to-strings *url-regex* "yo mailto:will@foo.com asd http://foo.com")
"mailto:will@foo.com"                                                                                                                         
#("mailto:will@foo.com" "mailto:will@foo.com" "mailto:" "")

/etc/passwd

中查詢使用者

[編輯 | 編輯原始碼]

進一步閱讀

[編輯 | 編輯原始碼]

http://www.weitz.de/cl-ppcre/ Edi Weitz 的 CL-PPCRE 頁面
華夏公益教科書