这个项目是《Python基础教程》后面的项目之一。当从一个不想写HTML代码的人那里得到一个文本文件,希望把它转成HTML的格式来当作一个网页来使用。人工添加标签是繁琐的,因此想写一个程序来自动完成。这类问题称之为即时标记。
主要是应用Python出色的文本处理能力——使用正则表达式将纯文本文件改写成语言(如HTML)中的标记,要做的工作基本上是将各种文本元素分类,比如标题和被强调的文本,然后明确地标记它们。
使用的方法:
1.对文件进行读写,从标准注入读取(sys.stdin),利用print输出等。
2.对所输入的行进行迭代。
3.使用了一些字符串方法。
4.re模块
程序分为四个模块:语法分析器、规则、过滤器以及处理程序。
语法分析器:读取文本,管理其他类。
规则:为每个种类的块定制规则,检测适用的块类型并且进行适当的格式化。
过滤器:包装一些处理内嵌元素的正则表达式。
self.addFilter(r'\*(.+?)\*', 'emphasis') self.addFilter(r'(http://[\.a-z0-9A-Z/]+)', 'url') self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[a-zA-Z]+)','mail')这三个过滤器分别是关于强调的内容,关于URL,关于email。
处理程序:产生不同种类的标记。
具体实现代码:
处理程序(handles.py):
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 substitution(match): result = self.callback('sub_', name, match) if result is None: match.group(0) return result return substitution class HTMLRenderer(Handler): def start_document(self): print '<html><head><title>...</title></head><body>' def end_document(self): print '</body></html>' def start_paragraph(self): print '<p>' def end_paragraph(self): print '</p>' def start_heading(self): print '<h2>' def end_heading(self): print '</h2>' 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 start_title(self): print '<h1>' def end_title(self): print '</h1>' def sub_emphasis(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_mail(self, match): return '<a href="mailto:%s">%s</a>' % (match.group(1), match.group(1)) def feed(self, data): print data
规则(rules.py):
class Rule: """ """ def action(self, block, handler): handler.start(self.type) handler.feed(block) handler.end(self.type) return True class HeadingRule(Rule): """ """ type = 'heading' def condition(self, block): return not '\n' in block and len(block) <= 70 and not block[-1] == ':' class TitleRule(HeadingRule): """ """ type = 'title' first = True def condition(self, block): if not self.first: return False self.first = False return HeadingRule.condition(self, block) class ListItemRule(Rule): """ """ 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): """ """ 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) self.inside = False return False class ParagraphRule(Rule): """ """ type = 'paragraph' def condition(self, block): return True
主程序(markup.py):
import sys, re from handlers import * from util import * from rules import * class Parser: 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): self.handler.start('document') for block in blocks(file): 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): """ """ def __init__(self, handler): Parser.__init__(self, handler) self.addRule(ListRule()) self.addRule(ListItemRule()) self.addRule(TitleRule()) self.addRule(HeadingRule()) self.addRule(ParagraphRule()) self.addFilter(r'\*(.+?)\*', 'emphasis') self.addFilter(r'(http://[\.a-zA-Z/]+)', 'url') self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+)', 'mail') handler = HTMLRenderer() parser = BasicTextParser(handler) parser.parse(sys.stdin)
补充util.py
def lines(file): for line in file:yield line yield '\n' def blocks(file): block = [] for line in lines(file): if line.strip(): block.append(line) elif block: yield ''.join(block).strip() block = []
运行结果:
被处理的文本:
处理后的结果为: