• 第20章 项目1:即时标记


    参考:http://www.th7.cn/Program/Python/201506/491157.shtml
    	     http://www.cnblogs.com/isuifeng/p/5839748.html

    20-1 test_input.txt

    Welcome to World Wide Spam, Inc.

    These are the corporate weg pages of *World Wide Spam*, Inc. We hope
    you find your stay enjoyable, and that you will sample any 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 emails. 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 was SPAM? (http://wspam.fu/whatisspam)
    - How do they make it? (http://wspam.fu/howmakeit)
    - Why should I eat it? (http://wspam.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://wspam.fu/feedback).

    20-2 util.py

    # coding=utf-8
    #文本块生成器
    def lines(file): #生成器,for循环时会依次返回每一行,它只在文件的最后追加了一个空行
    '''
    该函数从file中读取每一行并建立迭代对象,并在末尾加上换行符
    简而言之,就是凡是包含yield的函数,都可当作for的对象使用,lines(file)生成器a = lines(file),a可以作为for关键字的对象,a还可以使用__next__内建属性
    之所以最后加上一个换行符,是为了block中的判断,将最后一个块输出,
    如果去掉yield ' ' 并且读的文件中最后一行不为空,会发现最后块无法输出
    '''
    for line in file: yield line
    yield ' '
    # 生成器,for循环时会依次返回文本块组成的函数
    def blocks(file): # 读取文件,将文件分成块
    block = []
    for line in lines(file): #读取文件的每一行
    if line.strip(): #去掉string的前后端、空格,判断是否为真
    block.append(line) #如果为真,将string添加到block中
    elif block: #如果line为假,判断block是否为空。
    yield ''.join(block).strip() # 如果不为空,将block中所有元素连接起来为一个新的string,元素之间的连接字符为空去掉新string的前后端空格, 将新string添加到生成器中
    block = [] #重新将block赋值为空,以便读取后面内容

    首先我们来看lines()方法,参数是文件,然后对文件进行循环,每次读取一行文件,主意这离的yield关键字,这里代表方法是一个生成器,循环的时候到yield我们可以理解成返回一次line内容。文本读完后,yield处是一个' '

    blocks()方法就使用了上面的生成器,每次循环取出内容后,对line内容进行判断,如果有值,去除两边空格,添加到列表中,否则将block列表生成字符串。我们可以看出blocks也是一个生成器,它的实际功能是从文件中依次读取出来一个文本块。

    20-3 simple_mark.py
    # coding=utf-8
    #一个简单的标记程序
    import sys, re
    from util import *

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

    title = True
    for block 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>'

    这里我们需要注意一下,re.sub(r'*(.+?)*',r'<em>1</em>',block),是re模块的应用,首先增则匹配到内容,然后替换式替换。

    其他部分,就是判断title是否为True,若是则给h1标签,否则给p标签。

    cmd中使用如下指令运行:

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

    python表示用python.exe 运行simple_markup.py

    <表示从<之后的描述符输入,即从test_input.txt输入
    >
    表示从>之后的描述符输出,即输出到test_output.html

    可以看出生成一个test_output.html文件

    用浏览器打开该文件以后,出现如下界面

    对比发现,即时标记器作了如下修改:

    1. 将单行转化为h1的标题

      2. 将多行转化为段落p

      3. 将带有"-"的行转化为列表

      4. 将链接文本转化为html中的超链接

      5. *号之间的字符转换为斜体

      6. 去掉了多余的换行

       

      20-4 handlers.py handlers模块

      # coding=utf-8
      class Handler:
      """
      调用方法的处理类,处理从Parser调用的方法的对象。
      这个解析器会在每个块的开始部分调用start()和end()方法,使用合适的块名作为参数。
      sub()方法会用于正则表达式替换中。当使用了'emphasis'这样的名字调用时,它会返回合适的参数
      """
      # 判断当前类是否有对应的方法,有的话则根据提供的额外参数使用对应方法
      # 定义回调函数,调用该类中函数名为prefix+name的方法,输入参数为*args
      def callback(self, prefix,name,*args):
      method = getattr(self, prefix+name, None)
      if callable(method): return method(*args)
      # callback的辅助方法,前缀就是start,只需要提供方法名即可
      def start(self, name):
      self.callback('start_', name)
      # 前缀为end的callback辅助方法
      def end(self, name):
      self.callback('end_', name)
      # 返回方法名subsutitution
      def sub(self, name):
      # 定义substitution函数,该函数将作为re.sub函数的第二个参数,作用是替换指定规则的字符串
      def substitution(match):
      result = self.callback('sub_', name, match)
      if result is None: result = match.group(0)
      return result
      return substitution

      class HTMLRenderer(Handler):
      """
      用于生成HTML的具体处理程序,Handler类的子类,html渲染类,
      HTMLRenderer内的方法都可以通过超类处理程序的start()、end()和sub()方法来访问。实现了添加HTML文档的基本标签
      """
      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) # 匹配字符串转换为html强调字符

      def sub_url(self, match):
      return '<a href="%s">%s</a>' % (match.group(1), match.group(1)) # group(1)表示匹配子组1

      def sub_mail(self, match):
      # 之所以用子组1是添加过滤器时设置了子组
      return '<a href="mailto:%s">%s</a>' % (match.group(1), match.group(1))

      def feed(self, data):
      print(data)


      1. Handler类中有四个方法,其中重点是callbacksub

      (1)Callback:两个必需参数,一个额外参数。

      getAttr()用来判断类中是否存在prefix+name的方法,若存在返回prefix+name,否则返回None

      ‚callable()用来判断方法是否可以调用,若可以调用,则给予参数*args并且调用,*args的含义是额外参数

      ƒstartend是包装了callback的两个方法,不细表。

      (2)Sub:目的是返回一个函数作为re.sub的替换函数,这样re.sub就不是写死的了。其中定义了一个substitution方法,实际上调用后返回的就是这个方法,也就是我们后面re.sub中需要用到的替换函数。

      2. HTMLRenderer类继承了Handler类,其中主要定义了一些用来输出的方法.

      htmlRenderer.py ——HTMLRenderer实例

      from handlers import HTMLRenderer
      handler = HTMLRenderer()
      print handler.sub('emphasis')
      输出:
      <function substitution at 0x000000000000006C>

      这里需要注意的是,re.sub的第二个参数可以是一个函数作为替换式,替换式的参数就是re.sub的第一个参数匹配后返回的正则对象

      添加代码:

      import re
      print re.sub(r'*(.+?)*', handler.sub('emphasis'), 'This *is* a test')

      输出:This <em>is</em> a test

      如果(假设)处理程序是LaTex Renderer (?????????不知道是什么)

      依然使用代码:

      print re.sub(r'*(.+?)*', handler.sub('emphasis'), 'This *is* a test')

      输出应为:This em{is} a test

       

      20-5 rule.py

      # coding=utf-8
      class Rule:
      """
      所有规则的基类,定义了两个方法,condition和action。
      condition接受一个文本块作为参数,通过返回布尔值来表示文本块是否适合当前的规则。
      action接受文本块和处理程序对象作为参数,用来对文本块执行操作,进行输出。
      """
      def action(self, block, handler):
      handler.start(self.type)
      handler.feed(block)
      handler.end(self.type)
      return True

      class HeadingRule(Rule):
      """
      标题占一行,最多70个字符,并且不以冒号结尾
      """
      type = 'heading'
      def
      condition(self, block): #不包含 ,也就是说并非最后一个块;长度小于70;不以冒号结尾
      return not ' ' in block and len(block) <= 70 and not block[-1] == ':'

      class
      TitleRule(HeadingRule):
      """
      题目是文档的第一个块,但前提是它是大标题。
      """
      type = 'title'
      # 只工作一次,处理第一个快,因为处理完一次之后first的值被设置为了False,所以不会再执行处理方法了
      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

      基类Rule定义了两个方法,conditionaction

      condition接受一个文本块作为参数,通过返回布尔值来表示文本块是否适合当前的规则。

      action接受文本块和处理程序对象作为参数,用来对文本块执行操作,进行输出。

      集成的类都不复杂,这里单独说一下ListRule

      这里定义了一个变量insideTrue,我们可以理解这个变量的意思是—List列表开始。因为在htmlList中还会包含节点,也就是这里的ListItem,所以他会在遇到一个列表项的时候触发一次,然后在最后一个列表项的时候再次触发。所以inside作为一个标志位,用来进行判断符合规则的文本块时需要执行start还是end方法。

      20-6 markup.py

      # coding=utf-8
      #主程序
      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): #创建过滤器,实际上这里return的是一个替换式
      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]+[a-zA-Z]+)', 'mail')

      handler = HTMLRenderer()
      parser = BasicTextParser(handler)
      parser.parse(sys.stdin)

       

      运行未报错,出现:<html><head><title>...</title></head><body>

      先看基类Parser,构造函数需要一个handler对象作为参数,以供全局调用,同时初始化了两个列表。

      addRuleaddFilter的目的是向规则和过滤器列表添加元素。

      parse方法,读取文本文件,循环出每一个文本块,先通过过滤器过滤,然后执行相应规则。

      我们注意,规则和按照列表依次执行的,会判断返回值,若为False则不再对文本块执行后续规则了。

      BasicTextParser类的构造函数,只是在基类的基础上增加了向规则和过滤器列表添加具体内容的步骤。

      然后初始化类,并且对文件执行parse方法,即时标记项目完成。

       

      下面在cmd中使用指令:

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

      输出一个final_output.html文件

      打开界面如下:

  • 相关阅读:
    去除UINavigationBar的下边框
    struts2配置log
    GET和POST方式的乱码问题
    Eclipse中导入MyEclipse生成的war包
    TOMCAT管理页面部署WAR包 大小超出上限
    Java死锁 Thread Dump分析
    自定义标签之IterationTag
    Chart.js学习
    计数排序
    练手系列之旋转字符串
  • 原文地址:https://www.cnblogs.com/Sumomo0516/p/6131632.html
Copyright © 2020-2023  润新知