1.Python脚本学习实战笔记一 即时标记

1.Python脚本学习实战笔记一 即时标记

                  本篇名言:“复杂的事情简单做。简单的事情认真做。认真的事情重复做。重复的事情创造性地做。过去的快乐留待回忆,未来的快乐正在计划,但快乐只能现在感受!”

1. 需求

如何为纯文本添加一些格式?例如为纯文本添加标签成为html,使得到的文档能在浏览器中显示并能作为一个网页使用。

                  原型的目标:

l  输入不应该包含人工代码或者标签

l  应该能处理不同的块

l  处理的是HTML,但是能扩展到其他标记语言

初次实现目标并不能全部实现,这就是原型的意义,编写原型找出最开始想法中的纰漏。

2. 工具及准备

l  需要读写文件(sys.stdin,print输出)

l  行迭代

l  字符串方法

l  生成器

l  正则表达式re模块

 

准备测试文档如下:

Welcome to World Wide Spam, Inc.

 

 

These are the corporate web pages of *World Wide Spam*, Inc. We hope

you find your stay enjoyable, and that you will sample many of our

products.

 

A short history of the company

 

World Wide Spam was started in the summer of 2000. The business

concept was to ride the dot-com wave and to make money both through

bulk email and by selling canned meat online.

 

After receiving several complaints from customers who weren't

satisfied by their bulk email, World Wide Spam altered their profile,

and focused 100% on canned goods. Today, they rank as the world's

13,892nd online supplier of SPAM.

 

Destinations

 

From this page you may visit several of our interesting web pages:

 

  - What is SPAM?(http://wwspam.fu/whatisspam)

 

  - How do they make it?(http://wwspam.fu/howtomakeit)

 

  - Why should I eat it?(http://wwspam.fu/whyeatit)

 

How to get in touch with us

 

You can get in touch with us in *many* ways: By phone (555-1234), by

email (wwspam@wwspam.fu) or by visiting our customer feedback page

(http://wwspam.fu/feedback).

3. 初次实现

3.1      找出文本块

遇到空行后,就返回已经收集的行。

那些返回的行就是块。当然不能返回空块。

文本块生成模块代码如下:

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 = []

 

定义了2个函数lines和blocks,lines返回行的生成器,blocks返回块的生成器。就是这么简单。

3.2      添加标记

l  打印开始标记

l  打印段落标签起来的块

l  打印结束标记

代码实现如下:

importsys, re

from utilimport *

 

print'<html><head><title>...</title><body>'

 

title = True

forblock in blocks(sys.stdin):

   block = re.sub(r'\*(.+?)\*', r'<em>\1</em>', block)

   if title:

        print'<h1>'

        print block

        print'</h1>'

        title = False

   else:

        print'<p>'

        print block

        print'</p>'

 

print'</body></html>'

代码从标准输入中得到测试文档,并调用blocks函数得到块的生成器。

然后将每行在**之间的文字改变字体。

对一个块变成TITILE,其他的块都是文本。

按如下命令执行

python simple_markup.py<test_input.txt> test_output.html

输出结果test_output.html

双击test_output.html打开如下:

 

第一次原型实现了将文本分解成块,然后为每个块应用一个过滤器。

成功将纯文本转换成了HTML。

接下去要对原型进行扩展,如果发现继续扩展存在巨大问题,那么就需要重写。

4. 再次实现

如果想让程序有好的扩展性,就要使它模块化,把功能都分解到单独的模块中。实现模块性的方法之一就是使用面向对象的设计。

几个模块:

l  语法分析器。读取文本,管理其他类的对象

l  规则。为每个种类的块制定规则,规则检测适用的块类并且进行适当的格式化。

l  过滤器。过滤器来包装一些处理内嵌元素的正则表达式

l  处理程序。语法分析器适用处理程序来产生输出。每个处理程序能产生不同种类的标记。

                  从处理程序开始。

4.1      处理程序

处理程序要负责产生标记文本,接受来自文本分析器的具体指令。

假设每个类型的块都有一对方法,用于开始块和结束块。

可以设计成start_****,end_****

其中****表示块的类型。

在每个块的开始部分调用start()和end()方法,使用适合的块名作为参数。Sub()方法用于正则表达式替换中。

 

4.2      处理程序的超类

为了增加灵活性,加入一个Handler类,称为处理程序的超类,处理一些管理的细节。

实现如下:

class Handler:

    """

    An object that handlesmethod calls from the Parser.

 

    The Parser will call thestart() and end() methods at the

    beginning of each block,with the proper block name as a

    parameter. The sub() methodwill be used in regular expression

    substitution. When calledwith a name such as 'emphasis', it will

    return a propersubstitution function.

    """

    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):

        defsubstitution(match):

            result =self.callback('sub_', name, match)

            if result is None:match.group(0)

            return result

        return substitution

 

Callback方法负责在给定一个前缀和一个名字后查找正确的方法。如果对象能被调用,那么对象就返回可以用提供的任何额外的参数调用。

Start_,end_方法是使用各自的前缀start_和end_调用callback方法的助手的方法。

Sub 方法不直接调用callback, 而是返回一个新的函数。

具体的处理程序HTMLRender继承于Handler,通过父类提供的函数的来分发start,end,sub方法。

这样就让处理程序拥有了很好的扩展性和灵活性。

后续可以实现转换为其他的格式。

4.3      规则

规则在主程序中,主程序要决定对给定的块使用什么样的规则,让每个规则对块做需要的转换。规则具备如下功能:

l  识别自己适用于哪种块

l  能对块进行转换

所以每个规则对象都有两个方法:condition和action.

Condition 方法需要一个参数(所涉及的块),返回一个布尔值来表示规则是否适用于给定的块。

                  Action方法也要块作为参数,必须访问处理程序对象。

                  一个只有一个规则适合,如果发现标题规则合适,那么就不应该尝试段落规则。让语法分析器一个个去试这些规则,一旦规则被触发就停止处理块。(需要考虑使用异常,使停止处理当前块的规则),标题规则如下:

class HeadingRule(Rule):

    """

    A heading is a single linethat is at most 70 characters and

    that doesn't end with acolon.

    """

    type = 'heading'

    def condition(self, block):

        return not '\n' inblock and len(block) <= 70 and not block[-1] == ':'

 

4.4      规则的超类

由于规则存在一个共同的操作,用合适雷系的字符串参数调用处理程序的start,feed和end方法。可以给所有子类设置一个type特性,判断字符串形式的类型名。

 

 

4.5      过滤器

在Handler方法中实现sub方法,每个过滤器能使用一个正则表达式和一个名字来表示。

4.6      语法分析器

语法分析器是应用的核心。处理一个处理程序,一系列规则和过滤器将纯本文转换成标记文件。

                  需要几个方法,一个负责创建的构造器、一个添加规则的方法、一个添加过滤器的方法,以及一个对给定文件进行语法分析的方法。

                  addRule方法把规则添加到规则列表中。addFilter方法向过滤器列表中添加一个过滤器。

                 

 

 

 

 

4.7      构造规则和过滤器

可以编写一些单独的规则和过滤器,然后通过addRule和addFilter方法分别把它们加入到语法分析器中。确保在处理程序实现了合适的方法。

                  复杂的规则能处理复杂的文档。

                  标题的规则如下:

class HeadingRule(Rule):

    """

    A heading is a single linethat is at most 70 characters and

    that doesn't end with acolon.

    """

    type = 'heading'

    def condition(self, block):

        return not '\n' inblock and len(block) <= 70 and not block[-1] == ':'

特性类型被设置为字符串‘heading’.

所有的规则操作都返回True.

过滤器只是正则表达式。应用中增加了3个过滤器,一个关于强调,一个关于URL,一个关于电子邮件地址。

       self.addFilter(r'\*(.+?)\*','emphasis')

       self.addFilter(r'(http://[\.a-zA-Z/]+)', 'url')

       self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[a-zA-Z]+)', 'mail')

 

 

4.8      交付

创建一个Parser对象,并添加相关的规则和过滤器。在构造函数中初始化Parser的子类。然后使用它去分析sys.stdin.

运行

python markup.py < test_input.txt >test_output.html

然后双击test_output.html.

最后输出如下:

 

 

5. 源码

 

5.1      Handlers.py

class Handler:

    """

    An object that handlesmethod calls from the Parser.

 

    The Parser will call thestart() and end() methods at the

    beginning of each block,with the proper block name as a

    parameter. The sub() methodwill be used in regular expression

    substitution. When calledwith a name such as 'emphasis', it will

    return a propersubstitution function.

    """

    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):

        defsubstitution(match):

            result =self.callback('sub_', name, match)

            if result is None:match.group(0)

            return result

        return substitution

 

class HTMLRenderer(Handler):

    """

    A specific handler used forrendering HTML.

 

    The methods in HTMLRendererare accessed from the superclass

    Handler's start(), end(),and sub() methods. They implement basic

    markup as used in HTMLdocuments.

    """

    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 '<ahref="%s">%s</a>' % (match.group(1), match.group(1))

    def sub_mail(self, match):

        return '<ahref="mailto:%s">%s</a>' % (match.group(1), match.group(1))

    def feed(self, data):

        print data

 

 

5.2      Rules.py

class Rule:

    """

    Base class for all rules.

    """

    def action(self, block,handler):

       handler.start(self.type)

        handler.feed(block)

        handler.end(self.type)

        return True

 

class HeadingRule(Rule):

    """

    A heading is a single linethat is at most 70 characters and

    that doesn't end with acolon.

    """

    type = 'heading'

    def condition(self, block):

        return not '\n' inblock and len(block) <= 70 and not block[-1] == ':'

 

class TitleRule(HeadingRule):

    """

    The title is the firstblock in the document, provided that it is

    a heading.

    """

    type = 'title'

    first = True

 

    def condition(self, block):

        if not self.first:return False

        self.first = False

        returnHeadingRule.condition(self, block)

 

class ListItemRule(Rule):

    """

    A list item is a paragraphthat begins with a hyphen. As part of

    the formatting, the hyphenis removed.

    """

    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):

    """

    A list begins between ablock that is not a list item and a

    subsequent list item. Itends after the last consecutive list

    item.

    """

    type = 'list'

    inside = False

    def condition(self, block):

        return True

    def action(self, block,handler):

        if not self.inside andListItemRule.condition(self, block):

           handler.start(self.type)

            self.inside = True

        elif self.inside andnot ListItemRule.condition(self, block):

           handler.end(self.type)

            self.inside = False

        return False

 

class ParagraphRule(Rule):

    """

    A paragraph is simply ablock that isn't covered by any of the

    other rules.

    """

    type = 'paragraph'

    def condition(self, block):

        return True

 

 

5.3      Markup.py

import sys, re

from handlers import *

from util import *

from rules import *

 

class Parser:

    """

    A Parser reads a text file,applying rules and controlling a

    handler.

    """

    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):

            returnre.sub(pattern, handler.sub(name), block)

       self.filters.append(filter)

    def parse(self, file):

       self.handler.start('document')

        for block inblocks(file):

            for filter inself.filters:

                block = filter(block,self.handler)

            for rule inself.rules:

                ifrule.condition(block):

                    last =rule.action(block, self.handler)

                    if last:break

       self.handler.end('document')

 

class BasicTextParser(Parser):

    """

    A specific Parser that addsrules and filters in its

    constructor.

    """

    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]+[a-zA-Z]+)', 'mail')

 

handler = HTMLRenderer()

parser = BasicTextParser(handler)

 

parser.parse(sys.stdin)

 

 

6. 相关函数

6.1      Re.sub

re是regular expression的所写,表示正则表达式,sub是substitute的所写,表示替换;

re.sub是个正则表达式方面的函数,用来实现通过正则表达式,实现比普通字符串的replace更加强大的替换功能;

re.sub共有五个参数。

其中三个必选参数:patternreplstring

两个可选参数:countflags

Repl也是第二个参数可以是字符串,也可以是函数

 

 

6.2      getattr

该函数使用如下函数即可明白:

class A:  

   def __init__(self):  

        self.a = 'a' 

   def method(self):  

        print"method print" 

 

a = A()  

printgetattr(a, 'a', 'default')

printgetattr(a, 'b', 'default'

printgetattr(a, 'method', 'default')

print getattr(a, 'method', 'default')()

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值