Домашно 2 – Целувката на хипертекстовия метаезик

„ Програмиране с Python“, ФМИ

15.4.2010

Що е то?

Що е то?

Не пишете kiss() ако можете

Подход: Parser

  1. Консумираме входящият текст по елемeнти (tokens)
  2. Обръщаме списъка с елементи в дървовидна структура с автомат
  3. Обхождаме дървото за да генерираме HTML

Подход: Parser

Подход: „Заменки“

def kiss(text):
    replacements = [("\n======", '<h6>'), ("======\n", '</h6>'),
         ("\n=====", '<h5>'), ("=====\n", '</h5>'), ...
         ("\n* ",'<li>'), ...
         ('http://', '<a href="http://'), ...
         ]
    for keyword, substitute in replacements:
	    text = text.replace(keyword, substitute)
    return text

Подход: Разделяме на функции

Подход: Колекция от регулярни изрази

def kiss(text):
	patterns = [
         (регулярен израз, заменка),
         (регулярен израз, заменка),
         ...
    ]

    for pattern, substitute in replacements:
	    text = re.sub(keyword, substitute, text, 
                   flags = re.MULTILINE)

	return text

HTML

Пълен пример 1/2

def kiss(text):
  domain_re = r'([a-z0-9-]+\.)+[a-z0-9-]+'
  patterns = (
    (r'^#.*\n?', ''), # коментари
    (r'(^\*.*\n){2,}', # списъци 
        lambda m: '\n\n<ul>'+sub(r'^\*(.*)$', r'<li>\1</li>', 
        m.group(0))+'</ul>\n\n'),
    (r'^(=+)(.*)\1?$', # заглавия
        lambda m: '\n\n<h%(level)d>%(heading)s</h%(level)d>\n\n' 
        % {'level': len(m.group(1)), 'heading': m.group(2)}),
    (r'(http://%s(/([a-z0-9/_.?&#-]*[a-z0-9/_&#-])?)?)' # адреси
        % domain_re, r'<a href="\1">\1</a>'),
    (r'([a-z0-9._+]+@%s)' % domain_re, # email адреси
        r'<a href="mailto: \1">\1</a>'),
    (r'\[([a-z]+)\]', r'<span class="\1">'), # специален текст
    (r'\[/[a-z]+\]', r'</span>'), # специален текст
    (r'\n{2,}', '</p><p>'), # 2+ нови реда
    (r'\n', '<br />'), # 1 нов ред

Пълен пример 2/2

    # оправяме сбъркания HTML
    (r'<p>\s*(<ul>|<h\d>)', r'\1'),
    (r'</ul>\s*</p>', '</ul>'),
    (r'(</ul>|</h\d>)\s*</p>', r'\1'),
    (r'(<br\s*/>\s*)+</p>', '</p>'),
    (r'(<p>\s*<br\s*/>)+', '<p>'),
    (r'(</li>\s*<br\s*/>)+', '</li>'),
    (r'<p>\s*</p>', ''),
  )
  text = '<p>\n' + text + '\n</p>'    
  for pattern, substitute in patterns:
   text = re.sub(keyword, substitute, text, 
     flags = re.MULTILINE)
  return text.strip()

Проблеми

    (r'\[([a-z]+)\]', r'<span class="\1">'), # специален текст
    (r'\[/[a-z]+\]', r'</span>'), # специален текст
    (r'\[([a-z]+)\](.*?)[/\1]', r'<span class="\1">\2</span>'), # специален текст

Повторения

  def parseH1():
     ... 10 реда код ...

  def parseH2():
     ... 10 реда код ...

  ...

  def parseH6():
     ... 10 реда код ...

str.replace() и re.sub()

# по-лошо
matches = re.findall(регулярен израз, text)
text.replace(matches[0] + matches[1], 
    "<ul><li>" + matches[1] + "</li>")

# по-добре
text = re.sub(регулярен израз, 
    "<ul><li>\1</li>" , text)

# \1 == matches[1]

Необезопасени регулярни изрази

* Винаги escape-вайте външен текст

email = "ivan12m@abv.bg"
re.sub(email, "mailto:" + email, text) # работи

email = "ivan12m+seksimacki@abv.bg"
re.sub(email, "mailto:" + email, text) # не работи

re.sub(re.escape(email), 
    "mailto:" + email, text) # работи

Редовни

if __name__ == '__main__':
    test_text = """..."""
    print(kiss(test_text))

Още въпроси?