-
定义文本和html对应的规则
-
决定规则对应的处理程序(handler)
-
handler.py:将给定规则的文本转换为html
-
parse.py:调用util.py读取文本,调用rule.py判断文本对应的规则,调用handler处理给定规则的文本
这样子基本脉络已经清晰了,看代码
util.py
# -*- coding=utf-8 -*-
def lines(file):
给每个段落强制添加一个空行
for line in file :
yield line
yield '\n'
def blocks(file):
将输入的文件按按空行分块
block = []
for line in lines(file):
if line.strip():
# line不是空行,属于一个块
block.append(line)
elif block:
# 当前行是空行,block不为空,块已经结束,返回块
yield ''.join(block).strip()
block = []
View Code
handler.py
# -*- coding=utf-8 -*-
class Handler:
def callback(self, prefix, name, *args):
method = getattr(self, prefix+name, None)
if callable(method):
return method(*args)
def start(self, name):
self.callback('start_', name)
def end(self, name):
self.callback('end_', name)
def sub(self, name):
def subtitution(match):
result = self.callback('sub_', name, match)
if result is None:
match.group(0)
return subtitution
class HTMLRender(Handler):
def start_document(self):
print '<html><head><title>...</title></head><body>'
def end_document(self):
print '</body>'
def start_paragraph(self):
print '<p>'
def end_paragraph(self):
print '</p>'
def start_head(self):
print '<h2>'
def end_head(self):
print '</h2>'
def start_title(self):
print '<h1>'
def end_title(self):
print '</h1>'
def start_list(self):
print '<ul>'
def end_list(self):
print '</ul>'
def start_listitem(self):
print '<li>'
def end_listitem(self):
print '</li>'
def sub_em(self, match):
return '<em>%s</em>' % match.group(1)
def sub_url(self, match):
return '<a href="%s">%s</a>' % (match.group(1), match.group(1))
def sub_email(self, match):
return '<a href="mailto:%s">%s</a>' % (match.group(1), match.group(1))
def feed(self, data):
print data
View Code
rule.py
# -*- coding=utf-8 -*-
class Rule:
def action(self, block, handler):
handler.start(self.type)
handler.feed(block)
handler.end(self.type)
return True
class HeadRule(Rule):
小标题的规则,没有换行符,长度小于70并且不以:结尾
type = 'head'
def condition(self, block):
return not '\n' in block and len(block) < 70 and block[-1] != ':'
class TitleRule(HeadRule):
标题的规则,第一行是就是标题
type = 'title'
first = True
def condition(self, block):
if not self.first:
return False
self.first = False
return HeadRule.condition(self, block)
class ListItemRule(Rule):
li的规则,如果block以-开头就是listitem,在显示的时候去除-
type = 'listitem'
def condition(self, block):
return block[0] == '-'
def action(self, block, handler):
handler.start(self.type)
handler.feed(block[1:].strip())
handler.end(self.type)
return True
class ListRule(ListItemRule):
ul的规则,第一个li开始之前,添加ul,在第一个不是li的时候结束ul
type = 'list'
inside = False
def condition(self, block):
return True
def action(self, block, handler):
if not self.inside and ListItemRule.condition(self, block):
handler.start(self.type)
self.inside = True
elif self.inside and not ListItemRule.condition(self, block):
handler.end(self.type)
# 每次返回False,在对block使用完本规则之后继续使用其他规则
return False
class ParagraphRule(Rule):
p段落规则,当前面所有的规则匹配的时候,默认使用本规则
type = 'paragraph'
def condition(self, block):
return True
View Code
parse.py
# -*- coding=utf-8
import re, sys
from util import *
from handler import *
from rule import *
import ipdb
class Parser:
解析器,利用rule对文本进行解析并转换为html,利用filter转换块内文本
def __init__(self, handler):
self.handler = handler
self.rules = []
self.filters = []
def addRule(self, rule):
self.rules.append(rule)
def addFilter(self, pattern, name):
def filter(block, handler):
return re.sub(pattern, handler.sub(name), block)
self.filters.append(filter)
def parse(self, file):
#pdb.set_trace()
self.handler.start('document')
for block in blocks(file):
ipdb.set_trace()
for filter in self.filters:
block = filter(block, self.handler)
for rule in self.rules:
if rule.condition(block):
last = rule.action(block, self.handler)
if last:
break
self.handler.end('document')
class BasicTextParser(Parser):
基本文本解析器,只有h1,h2,p,ul,li,a,em,email
def __init__(self, handler):
Parser.__init__(self, handler)
self.addRule(ListRule())
self.addRule(ListItemRule())
self.addRule(TitleRule())
self.addRule(HeadRule())
self.addRule(ParagraphRule())
self.addFilter(r'\*(.+?)\*', 'em')
self.addFilter(r'(http://[\.a-zA-Z]+)', 'url')
self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[[a-zA-Z]+)', 'email')
handler = HTMLRender()
parser = BasicTextParser(handler)
parser.parse(sys.stdin)
View Code
第一次练习就遇到这个项目显得略大,不过搞清楚思路之后整体把我就没问题了,在练习的过程中还遇到一些问题、学到一些东西,总结如下:
-
子类调用父类的方法(包括__init__)
父类名.方法名(self, 参数)
A类调用B类方法
b = B()
b.方法(参数)
-
re模块的sub方法
re.sub(regex_str, replace_str, str)
将str中符合正则表达式regex_str的子串用replace_str代替
如果replace_str是正则表达式,可以使用分组替换,例如
re.sub(r'\*(.+)\*', r'---\1---', "asdasdasd*nskjdfnkjdsnf*sdfsdf")
re.sub(r'\*(.+)\*',lambda m: "---" + m.group(1) + "---" ,"asdasdasd*nskjdfnkjdsnf*sdfsdf")
其实作者里面有两个思想让我触动很深,甚是佩服:
-
一个模块做最少的事,制作自己必须做的事
-
事件的路由分发(借由python发挥的淋漓尽致)
这两种思想让项目整体架构变的很清晰,对扩展也很友好,大写的赞!
这个练习已经写完几天了,今天补上记录一下,代码位置
http://pan.baidu.com/s/1o7JVKKU