提交 3de50ae0 编写于 作者: H HypoX64

Finish Automata

上级 86c4778b
...@@ -11,7 +11,6 @@ input_strings = myre.split(input_txt, [' ',',','"','.','(',')']) ...@@ -11,7 +11,6 @@ input_strings = myre.split(input_txt, [' ',',','"','.','(',')'])
pattern = 's{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}*n' pattern = 's{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}*n'
# 或者 pattern = 's[a-z]*n' # 或者 pattern = 's[a-z]*n'
myre.match
for string in input_strings: for string in input_strings:
if myre.match(pattern, string.lower()): if myre.match(pattern, string.lower()):
print(string) print(string)
此差异已折叠。
import myre import myre
""" """
规则 对应所支持的语法
"." : 用于匹配任意单个字符 "." : 用于匹配任意单个字符
"c" : 匹配除了Token以外的输入的字符
"[a-z]" : 匹配a-z中的任意单个字符 "[a-z]" : 匹配a-z中的任意单个字符
"{a,b,c}" : 匹配a或b或c中的任意单个字符,也可以写作(a|b|c) "{a,b,c}" : 匹配a或b或c中的任意单个字符,也可以写作(a|b|c)
"*" : 闭包 "*" : 闭包
"+" : 正闭包 "+" : 正闭包
"|" : 或(加) "|" : 或(加)
"" : 默认进行连接操作(乘) "" : 默认进行连接操作(乘)
"("expr")": 支持使用括号提高运算优先级
""" """
# test 1 # test 1
......
...@@ -7,15 +7,16 @@ def match(pattern_string,input_string): ...@@ -7,15 +7,16 @@ def match(pattern_string,input_string):
return :: True | False return :: True | False
-------------------------------------------------------- --------------------------------------------------------
pattern规则 对应所支持的语法
"." : 用于匹配任意单个字符 "." : 用于匹配任意单个字符
"c" : 匹配除了Token以外的输入的字符
"[a-z]" : 匹配a-z中的任意单个字符 "[a-z]" : 匹配a-z中的任意单个字符
"{a,b,c}" : 匹配a或b或c中的任意单个字符,也可以写作(a|b|c) "{a,b,c}" : 匹配a或b或c中的任意单个字符,也可以写作(a|b|c)
"*" : 闭包 "*" : 闭包
"+" : 正闭包 "+" : 正闭包
"|" : 或(加) "|" : 或(加)
"" : 默认进行连接操作(乘) "" : 默认进行连接操作(乘)
"("expr")": 支持使用括号提高运算优先级
""" """
nfa_start_node = builder.create_nfa(pattern_string) nfa_start_node = builder.create_nfa(pattern_string)
return run.match(input_string, nfa_start_node) return run.match(input_string, nfa_start_node)
...@@ -34,6 +35,3 @@ def split(string,keys): ...@@ -34,6 +35,3 @@ def split(string,keys):
out_strings.append(string[cnt:i]) out_strings.append(string[cnt:i])
cnt = i+1 cnt = i+1
return out_strings return out_strings
def replace():
pass
\ No newline at end of file
...@@ -20,7 +20,7 @@ def create_nfa(pattern_string): ...@@ -20,7 +20,7 @@ def create_nfa(pattern_string):
词法分析按照优先级自顶向下 词法分析按照优先级自顶向下
expr ::= <factor_connect> ("|" factor_connect)* # "|" 加(或) expr ::= <factor_connect> ("|" factor_connect)* # "|" 加(或)
factor_connect ::= factor | factor factor* # "" 乘(直接连接) factor_connect ::= factor | factor factor* # "" 乘(直接连接)
factor ::= term | term ("*"|"+")* # "*"闭包 "+"正闭包 factor ::= term | term ("*"|"+")* # "*"闭包 "+"正闭包
term ::= char | group | "[" char "-" char "]" | "{" char "," char "}" | "." # 基本单元,终止符 term ::= char | group | "[" char "-" char "]" | "{" char "," char "}" | "." # 基本单元,终止符
group ::= "("expr")" # 递归解决括号优先级 group ::= "("expr")" # 递归解决括号优先级
""" """
...@@ -40,15 +40,15 @@ def term(nfa_cells): ...@@ -40,15 +40,15 @@ def term(nfa_cells):
""" """
对 . | a (单个字符) | 单个一定范围[a-z]的字符 | {a,b,c} 某些字符集合中的单个字符->相当于(a+b+c) 对 . | a (单个字符) | 单个一定范围[a-z]的字符 | {a,b,c} 某些字符集合中的单个字符->相当于(a+b+c)
""" """
if lexer.match(Token.L): # char if lexer.match(Token.L): # char
nfa_single_char(nfa_cells) nfa_single_char(nfa_cells)
elif lexer.match(Token.ANY): # . elif lexer.match(Token.ANY): # .
nfa_any_single_char(nfa_cells) nfa_any_single_char(nfa_cells)
elif lexer.match(Token.CCL_START): # [" char "-" char "] elif lexer.match(Token.SQUARE_START): # [" char "-" char "]
nfa_range_single_char(nfa_cells) nfa_range_single_char(nfa_cells)
elif lexer.match(Token.OPEN_CURLY): # "{" char "," char "}" elif lexer.match(Token.OPEN_CURLY): # "{" char "," char "}"
nfa_set_single_char(nfa_cells) nfa_set_single_char(nfa_cells)
elif lexer.match(Token.OPEN_PAREN): # "("expr")" elif lexer.match(Token.OPEN_PAREN): # "("expr")"
group(nfa_cells) group(nfa_cells)
def factor(nfa_cells): def factor(nfa_cells):
...@@ -100,7 +100,7 @@ def expr(nfa_cells): ...@@ -100,7 +100,7 @@ def expr(nfa_cells):
def nfa_single_char(nfa_cells): def nfa_single_char(nfa_cells):
""" """
L 匹配单个字符 L 匹配输入的单个字符
""" """
if not lexer.match(Token.L): if not lexer.match(Token.L):
return False return False
...@@ -117,7 +117,7 @@ def nfa_single_char(nfa_cells): ...@@ -117,7 +117,7 @@ def nfa_single_char(nfa_cells):
def nfa_any_single_char(nfa_cells): def nfa_any_single_char(nfa_cells):
""" """
. 匹配任意单个字符 . 匹配单个任意字符
""" """
if not lexer.match(Token.ANY): if not lexer.match(Token.ANY):
return False return False
...@@ -135,9 +135,9 @@ def nfa_any_single_char(nfa_cells): ...@@ -135,9 +135,9 @@ def nfa_any_single_char(nfa_cells):
def nfa_range_single_char(nfa_cells): def nfa_range_single_char(nfa_cells):
""" """
[a-z] 匹配范围字符集 [a-z] 匹配范围字符集中的单个字符
""" """
if not lexer.match(Token.CCL_START): if not lexer.match(Token.SQUARE_START):
return False return False
lexer.next() lexer.next()
start = nfa_cells.start_node = Cell() start = nfa_cells.start_node = Cell()
...@@ -147,7 +147,7 @@ def nfa_range_single_char(nfa_cells): ...@@ -147,7 +147,7 @@ def nfa_range_single_char(nfa_cells):
# get range char set # get range char set
first = '' first = ''
while not lexer.match(Token.CCL_END): while not lexer.match(Token.SQUARE_END):
if not lexer.match(Token.DASH): if not lexer.match(Token.DASH):
first = lexer.current_text first = lexer.current_text
start.char_set.add(first) start.char_set.add(first)
...@@ -162,7 +162,7 @@ def nfa_range_single_char(nfa_cells): ...@@ -162,7 +162,7 @@ def nfa_range_single_char(nfa_cells):
def nfa_set_single_char(nfa_cells): def nfa_set_single_char(nfa_cells):
""" """
{a,b,c....} 匹配字符集 相当于(a|b|c...) {a,b,c....} 匹配字符集中的单个字符 相当于(a|b|c...)
""" """
if not lexer.match(Token.OPEN_CURLY): if not lexer.match(Token.OPEN_CURLY):
return False return False
...@@ -208,7 +208,7 @@ def is_connect_token(token): ...@@ -208,7 +208,7 @@ def is_connect_token(token):
Token.CLOSURE, Token.CLOSURE,
Token.PLUS_CLOSURE, Token.PLUS_CLOSURE,
Token.CLOSE_CURLY, Token.CLOSE_CURLY,
Token.CCL_END, Token.SQUARE_END,
Token.OR, Token.OR,
] ]
return token not in no_connect return token not in no_connect
#边的类型 # 根据RE转NFA的规律边的类型分为3种
EPSILON = -1 # edge = EPSILON 对应的节点有两个出去的ε边 EPSILON = -1 # edge = EPSILON 对应的节点有两个出去的ε边
CCL = -2 # edge = CCL 边对应的是字符集(包括单个字符) ,需要结合属性char_set CCL = -2 # edge = CCL 边对应的是字符集(包括单个字符) ,需要结合属性char_set,自动机运行时只要当前读入的字符在char_set中就满足跳转条件
EMPTY = -3 # edge = EMPTY 一条ε边 EMPTY = -3 # edge = EMPTY 一条ε边
class Cell(object): class Cell(object):
......
...@@ -5,8 +5,8 @@ class Token(Enum): ...@@ -5,8 +5,8 @@ class Token(Enum):
END_OF_INPUT = 1 END_OF_INPUT = 1
ANY = 2 ANY = 2
L = 3 L = 3
CCL_START = 4 SQUARE_START = 4
CCL_END = 5 SQUARE_END = 5
OPEN_CURLY = 6 OPEN_CURLY = 6
CLOSE_CURLY = 7 CLOSE_CURLY = 7
CLOSURE = 8 CLOSURE = 8
...@@ -20,8 +20,8 @@ Tokens = { ...@@ -20,8 +20,8 @@ Tokens = {
'.': Token.ANY, '.': Token.ANY,
'(': Token.OPEN_PAREN, '(': Token.OPEN_PAREN,
')': Token.CLOSE_PAREN, ')': Token.CLOSE_PAREN,
'[': Token.CCL_START, '[': Token.SQUARE_START,
']': Token.CCL_END, ']': Token.SQUARE_END,
'{': Token.OPEN_CURLY, '{': Token.OPEN_CURLY,
'}': Token.CLOSE_CURLY, '}': Token.CLOSE_CURLY,
'*': Token.CLOSURE, '*': Token.CLOSURE,
...@@ -30,18 +30,35 @@ Tokens = { ...@@ -30,18 +30,35 @@ Tokens = {
'|': Token.OR, '|': Token.OR,
} }
"""
对应所支持的语法:
"." : 用于匹配任意单个字符
"c" : 匹配除了Token以外的输入的字符
"[a-z]" : 匹配a-z中的任意单个字符
"{a,b,c}" : 匹配a或b或c中的任意单个字符,也可以写作(a|b|c)
"*" : 闭包
"+" : 正闭包
"|" : 或(加)
"" : 默认进行连接操作(乘)
"("expr")": 支持使用括号提高运算优先级
"""
class Lexer(object): class Lexer(object):
"""
对正则表达式进行解析,校验正则表达式是否合法,得到对应字符的Token,目前不支持转义字符的输入
"""
def __init__(self, pattern): def __init__(self, pattern):
self.pattern = pattern self.pattern = pattern
self.pos = 0 self.pos = 0
self.current_text = '' self.current_text = ''
self.current_token = None self.current_token = None
self.check_pattern()
# self.del_paren() # self.del_paren()
# print(self.pattern) # print(self.pattern)
def next(self): def next(self):
""" """
返回Token并读入下一个字符 返回当前对应的Tokens并读入下一个字符
""" """
pos = self.pos pos = self.pos
pattern = self.pattern pattern = self.pattern
...@@ -61,6 +78,23 @@ class Lexer(object): ...@@ -61,6 +78,23 @@ class Lexer(object):
self.pos = self.pos + 1 self.pos = self.pos + 1
return Tokens.get(text, Token.L) return Tokens.get(text, Token.L)
def check_pattern(self):
"""
检查正则表达式括号是否匹配
"""
brackets = ['(',')','[',']','{','}']
cnt = [0,0,0,0,0,0]
for i in range(len(self.pattern)):
for j in range(len(brackets)):
if self.pattern[i] == brackets[j]:
cnt[j] += 1
if cnt[0] < cnt[1] or cnt[2] < cnt[3] or cnt[4] < cnt[5]:
print('Error: Please check the input pattern')
exit(0)
if cnt[0] != cnt[1] or cnt[2] != cnt[3] or cnt[4] != cnt[5]:
print('Error: Please check the input pattern')
exit(0)
def del_paren(self): def del_paren(self):
cnt = 0 cnt = 0
for i in range(len(self.pattern)): for i in range(len(self.pattern)):
...@@ -70,4 +104,7 @@ class Lexer(object): ...@@ -70,4 +104,7 @@ class Lexer(object):
cnt += 1 cnt += 1
def match(self, token): def match(self, token):
"""
将输入的token与当前的token进行比较
"""
return self.current_token == token return self.current_token == token
\ No newline at end of file
[toc]
## Question1
设计一个DFA,使其接受2进制字符串w,并且所有w的逆都能被5整除。比如,该DFA接受二进制串11110,因为其逆为01111,代表十进制的15,所以可以被5整除。详述该DFA的设计。
### 1.1 思路
首先设计一个接受的二进制串能被5整除的的DFA,然后将整个DFA的状态反转使其接受的为符合条件的二进制的逆。
### 1.2 设计一个接受的二进制串能被5整除的DFA
* 将已扫描到的字符的余数作为当前状态,一共有5种情况,即5个状态:Q0:余数为0;Q1余数为1......根据规律列出状态转移表如下:<br>
<div align="center">
<img src="./imgs/Q1状态转移图.jpg " alt="image" style="zoom:33%;" />
</div>
* 根据状态转移表绘制状态转移图:<br>
<div align="center">
<img src="./imgs/Q1_5.jpg " alt="image" style="zoom:50%;" />
</div>
### 1.3 反转状态转移图
* 将1.2中的DFA的状态反转(将箭头反向并将起始状态与接受状态对调)并根据q0的转移函数添加起始状态qs以防止出现未输入任何字符就接受的情况:<br>
<div align="center">
<img src="./imgs/Q1_5逆.jpg " alt="image" style="zoom:50%;" />
</div>
## Question2
使用归纳证明法证明对于任意的NFA M=(Q,Σ,δ,q0 ,F),一定存在CFG G=(Q,Σ,P,S),使得L(G)=L(M)。
### 2.1 思路
等价转化法:已知NFA与RE等价,故问题转化为对任意的正则表达式R,都可以用CFG G=(Q,Σ,P,S)来表示。即要证明L(R)=L(G)。<br>
### 2.2 文法设计
RE由运算符,字符集,以及'()'组成,故根据运算优先级可以得到文法G:<br>
```shell
S -> A
A -> B|A"|"B # "|" 加(或)
B -> C|BC # "" 乘(直接连接),幂运算可由乘实现
C -> D|D"*"|D"+"|CD"*"|CD"+" # "*"闭包 "+"正闭包
D -> E|F
E -> "a","b","c","d","e","f","g"...... #终结符
F -> "("A")" #括号递归
```
### 2.3 归纳法证明
对输入的正则表达式R的运算符数量进行归纳法证明。<br>
* 当n=1时:<br>
R = "a*" | "a|a" | "a+" | "aa" | "(aa)"......<br>
以R =(aa)为例的派生过程:<br>
S=>A=>B=>C=>D=>F=>"("A")"=>"("B")"=>"("BC")"=>"("CC")"=>"("CD")"=>"("DD")"=>"("DE")"=>"("EE")"=>"(""a""a"")"即S=>(aa)<br>
易知其他情况也满足。<br>
* 假设当运算符数量为n时,正则表达式R也是合法的G<br>
当运算符数量为n+1时:<br>
R' = "R*" | "R|a" | "R+" | "Ra" | "(Ra)"...<br>
由于G可以派生出R,故S=>A=>....R可得A=>R<br>
以R' ="R|a"的派生过程为例:<br>
S=>A=>A"|"B=>R"|"B=>R"|"C=>R"|"D=>R"|"E=>R"|""a"即S=>R‘,成立<br>
对其他情况依次进行归纳,均成立<br>
故对任意的正则表达式R,都可以由所定义的 G得到,即对于任意的NFA M=(Q,Σ,δ,q0 ,F),一定存在CFG G=(Q,Σ,P,S),使得L(G)=L(M)。
## Question3
正则表达式在信息检索中具有广泛的应用。请设计并编程实现一个基于正则表达式的英文单词检索系统,要求给定任意的正则表达式作为输入,将其转换为等价的自动机,并可以根据该自动机实现从输入文本中检测并输出所有符合正则表达式描述的单词。
例如,当输入文本文件input.txt包含以下英文文本及正则表达式```s{a,…,z}*n```时:
```
Shenzhen University (SZU, Chinese: 深圳大学) is a public university established in 1983 located in Nanshan district, Shenzhen, Guangdong, China. It is accredited by the State Council of the People's Republic of China and is funded by the Shenzhen City Government. The university took its first enrollment the same year at what Deng Xiaoping called "Shenzhen Speed". Deng also was named the "father of Shenzhen University." It is regarded as the fastest developing university in China, and also one of the "Top 100 Universities in China" and one of the top university which is listed in the World Top Ranking Universities.
```
输出:
```
Shenzhen
Shenzhen
Shenzhen
Shenzhen
Shenzhen
```
为所有符合该正则表达式描述得单词,注意,此处单词间以空格分隔,并且对大小写字母不敏感。请在提交报告中给出详细的设计方案、关键代码实现、以及实验测试结果。
### 3.1 思路
### 3.2 正则表达式引擎的设计
#### 3.2.1 整体流程
#### 3.2.2 NFA单个节点的定义
#### 3.2.3 语义分析
#### 3.2.4 NFA的构建
#### 3.2.3 词法分析即校验
### 3.3 实验测试结果
#### 3.3.1 正则表达式引擎基础测试
#### 3.3.2 测试input.txt
## Question4
请设计并编程实现一个简单分类器,要求各类别规则用正则表达式描述,实现输入任意一个字符串,自动给出该串的类别编号。例如定义 类别1:0*1*; 类别2: 1*0*。则给出串0011,程序将输出结果为类别1。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册