日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

实现一个正则表达式引擎in Python(二)

發(fā)布時間:2023/12/20 python 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实现一个正则表达式引擎in Python(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

項目地址:Regex in Python

在看一下之前正則的語法的 BNF 范式

group ::= ("(" expr ")")* expr ::= factor_conn ("|" factor_conn)* factor_conn ::= factor | factor factor* factor ::= (term | term ("*" | "+" | "?"))* term ::= char | "[" char "-" char "]" | .

上一篇構造了 term 的簡單 NFA

構造復雜的 NFA

factor

根據上面的factor ::= (term | term ("*" | "+" | "?"))*,先進行 term 的 NFA 的生成,然后根據詞法分析器來判斷要進行哪個 factor 的 NFA 的構造

def factor(pair_out):term(pair_out)if lexer.match(Token.CLOSURE):nfa_star_closure(pair_out)elif lexer.match(Token.PLUS_CLOSE):nfa_plus_closure(pair_out)elif lexer.match(Token.OPTIONAL):nfa_option_closure(pair_out)

nfa_star_closure

*操作就是對之前的 term 再生成兩個節(jié)點進行連接

def nfa_star_closure(pair_out):if not lexer.match(Token.CLOSURE):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodestart.next_2 = endpair_out.end_node.next_1 = pair_out.start_nodepair_out.end_node.next_2 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return True

nfa_plus_closure

+和*的唯一區(qū)別就是必須至少匹配一個字符,所以不能從節(jié)點 2 直接跳轉到節(jié)點 4

def nfa_plus_closure(pair_out):if not lexer.match(Token.PLUS_CLOSE):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodepair_out.end_node.next_1 = pair_out.start_nodepair_out.end_node.next_2 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return True

nfa_option_closure

?對應的則是只能輸入 0 個或 1 個的匹配字符,所以相對于*就不能再次從節(jié)點 1 跳轉會節(jié)點 0

def nfa_option_closure(pair_out):if not lexer.match(Token.OPTIONAL):return Falsestart = Nfa()end = Nfa()start.next_1 = pair_out.start_nodestart.next_2 = endpair_out.end_node.next_1 = endpair_out.start_node = startpair_out.end_node = endlexer.advance()return True

factor_conn

factor_conn ::= factor | factor factor*

對于 factor_conn 就是一個或者多個 factor 相連接,也就是說如果有多個 factor,只要將它們的頭尾節(jié)點相連接

def factor_conn(pair_out):if is_conn(lexer.current_token):factor(pair_out)while is_conn(lexer.current_token):pair = NfaPair()factor(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_nodereturn True

expr

expr ::= factor_conn ("|" factor_conn)*

對于 expr 就是一個 factor_conn 或者多個 factor_conn 用|相連接

構建|的 NFA 就是生成兩個新節(jié)點,新生成的頭節(jié)點有兩條邊分別連接到 factor_conn 的頭節(jié)點,對于兩個 factor_conn 的尾節(jié)點分別生成一條邊連接到新生成的尾節(jié)點

def expr(pair_out):factor_conn(pair_out)pair = NfaPair()while lexer.match(Token.OR):lexer.advance()factor_conn(pair)start = Nfa()start.next_1 = pair.start_nodestart.next_2 = pair_out.start_nodepair_out.start_node = startend = Nfa()pair.end_node.next_1 = endpair_out.end_node.next_2 = endpair_out.end_node = endreturn True

group

group 其實就是在 expr 上加了兩個括號,完全可以去掉

def group(pair_out):if lexer.match(Token.OPEN_PAREN):lexer.advance()expr(pair_out)if lexer.match(Token.CLOSE_PAREN):lexer.advance()elif lexer.match(Token.EOS):return Falseelse:expr(pair_out)while True:pair = NfaPair()if lexer.match(Token.OPEN_PAREN):lexer.advance()expr(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_nodeif lexer.match(Token.CLOSE_PAREN):lexer.advance()elif lexer.match(Token.EOS):return Falseelse:expr(pair)pair_out.end_node.next_1 = pair.start_nodepair_out.end_node = pair.end_node

構造 NFA 總結

可以看到對于整個 NFA 的構造,其實就是從最頂部開始向下遞歸,整個過程大概是:

  • expr -> factor_conn -> factor -> term

  • 當遞歸過程回到factor_conn會根據factor_conn ::= factor | factor factor*判斷可不可以繼續(xù)構造下一個factor

  • 如果不可以就返回到expr,expr則根據expr ::= factor_conn ("|" factor_conn)*
    判斷能不能繼續(xù)構造下一個factor_conn

  • 重復上面的過程

匹配輸入字符串

現在已經完成了NFA的構造,接下來就是通過這個NFA來對輸入的字符串進行分析

一個例子

以剛剛的圖作為演示,假設0-1節(jié)點的邊是字符集0-9,4-5節(jié)點的邊是字符集a-z,其它都是空

所以這個圖表示的正則表達式[0-9]*[a-z]+

假設對于分析字符串123a

  • closure

從開始節(jié)點8進行分析,我們要做的第一個操作就是算出在節(jié)點8時不需要任何輸入就可以到達的節(jié)點,這個操作稱為closure,得到closure集合

  • move

之后我們就需要根據NFA和當前的輸入字符來進行節(jié)點間的跳轉,得到的自然也是一個集合

closure操作

我們利用一個棧來實現closure操作

  • 把傳入集合里的所有節(jié)點壓入棧中
  • 然后對這個棧的所有節(jié)點進行判斷是否有可以直接跳轉的節(jié)點
  • 如果有的話直接壓入棧中
  • 直到棧為空則結束操作
def closure(input_set):if len(input_set) <= 0:return Nonenfa_stack = []for i in input_set:nfa_stack.append(i)while len(nfa_stack) > 0:nfa = nfa_stack.pop()next1 = nfa.next_1next2 = nfa.next_2if next1 is not None and nfa.edge == EPSILON:if next1 not in input_set:input_set.append(next1)nfa_stack.append(next1)if next2 is not None and nfa.edge == EPSILON:if next2 not in input_set:input_set.append(next2)nfa_stack.append(next2)return input_set

move操作

  • move操作就是遍歷當前的狀態(tài)節(jié)點集合,如果符合的edge的條件的話
  • 就加入到下一個狀態(tài)集合中
def move(input_set, ch):out_set = []for nfa in input_set:if nfa.edge == ch or (nfa.edge == CCL and ch in nfa.input_set):out_set.append(nfa.next_1)return out_set

match

現在最后一步就是根據上面的兩個操作進行字符串的分析了

  • 首先先計算出開始節(jié)點的closure集合
  • 開始遍歷輸入的字符串,從剛剛的closure集合開始做move操作
  • 然后判斷當前的集合是不是可以作為接收狀態(tài),只要當前集合有某個狀態(tài)節(jié)點沒有連接到其它節(jié)點,它就是一個可接收的狀態(tài)節(jié)點,能被當前NFA接收還需要一個條件就是當前字符已經全匹配完了
def match(input_string, nfa_machine):start_node = nfa_machinecurrent_nfa_set = [start_node]next_nfa_set = closure(current_nfa_set)for i, ch in enumerate(input_string):current_nfa_set = move(next_nfa_set, ch)next_nfa_set = closure(current_nfa_set)if next_nfa_set is None:return Falseif has_accepted_state(next_nfa_set) and i == len(input_string) - 1:return Truereturn False

小結

這篇主要講了復雜一點的NFA節(jié)點的構建方法,和對利用構造的NFA來對輸入自負床進行分析。到目前為止,其實一個完整的正則表達式引擎已經完成了,但是如果想更近一步的話,還需要將NFA轉換成DFA,再進行DFA的最小化

轉載于:https://www.cnblogs.com/secoding/p/11579650.html

總結

以上是生活随笔為你收集整理的实现一个正则表达式引擎in Python(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。