« 思いつき |Main| 日記 »

« PythonでHMMの勉強 | Python | Pythonでウィンドウをずらしながら平均、分散、回帰直線の傾き、相関係数を求める。 »

Pythonで箇条書きをHTMLに変換

COREBlog使用時も生DTMLでエントリーを書き、tDiaryの勝手な整形よりMovableTypeでタグを手打ちする道を選んだ僕ですが、それでも箇条書きをHTMLで書くのは直感的でなさ過ぎるので嫌いです。 そこで、MovableTypeに「Wikiっぽい記法で書いた箇条書きをHTMLに変換してくれるタグ」を追加しようと思い、とりあえずそのアルゴリズムをPythonで書いてみました。(Perlでいきなり書く自信がなかったので)

あ、誤解を避けるために書いておくと、もちろんPythonで構造化テキストを扱いたいだけならStructuredTextとかreStructuredTextで検索して適切なライブラリを使う方が楽だと思います。今回はあくまで自分好みの箇条書きフォーマットをHTMLに変換するPerlのプログラムを作るためのプロトタイプってことです。

  • 特徴
    • インデントの深さで階層構造を表現
      • オートインデント機能のあるエディタと親和性が高い
      • Pythonの制御構造は別に意識していません
        • でもPythonの構文に親しんだせいでこれが自然な表現方法と感じるのかも
      • 箇条書きの点がハイフンと1対1対応するので直感的にわかりやすい?
      • 点のついていない行は前の項目の続きになる
        • つまり一項目を無理矢理1行にまとめる必要はない
      • 箇条書き中に空行や空白文字だけの行を含めることができる
  • 文法
    • 空白文字だけの行は無視される
    • 行頭に最初に現れる空白文字以外の文字がハイフンでない場合直前の項目の続きだと見なされる。
    • 行頭に最初に現れる空白文字以外の文字がハイフンの時、新しいliタグが作成される。
      • その際、インデントが深くなっていればulタグを1つ挿入する。
      • インデントが浅くなっていれば、適切な個数の閉じulタグを挿入する。

上の箇条書きは下のソースから生成されました。 僕はたまたまスペース1個のインデントで統一していますけど、Python同様インデントの幅に制約はありません。スペース2個でインデントした方が子の頭が親の文章の始まりにそろって美しいかも知れませんね。

追記:ハイフンを取り除くのを忘れていたので修正しました。@2006-06-01 05:06

# -*- coding: cp932 -*-
#
# 箇条書きジェネレータ
#

data = """
- 特徴
 - インデントの深さで階層構造を表現
  - オートインデント機能のあるエディタと親和性が高い
  - Pythonの制御構造は別に意識していません
   - でもPythonの構文に親しんだせいで
     これが自然な表現方法と感じるのかも
  - 箇条書きの点がハイフンと1対1対応するので直感的にわかりやすい?
  - 点のついていない行は前の項目の続きになる
   - つまり一項目を無理矢理1行にまとめる必要はない
  - 箇条書き中に空行や空白文字だけの行を含めることができる

- 文法
 - 空白文字だけの行は無視される
 - 行頭に最初に現れる空白文字以外の文字がハイフンでない場合
   直前の項目の続きだと見なされる。
 - 行頭に最初に現れる空白文字以外の文字がハイフンの時、
   新しいliタグが作成される。
  - その際、インデントが深くなっていればulタグを1つ挿入する。
  - インデントが浅くなっていれば、適切な個数の閉じulタグを挿入する。
"""

MARK = "-"
import re
pat = re.compile("^(\s*).*")
indent_stack = [-1]
result = ""
for line in data.split("\n"):
    sline = line.strip()
    if sline == "":
        continue
    if sline[0] != MARK:
        result += sline
        continue
    r = pat.match(line)
    indent = len(r.groups()[0])
    body = sline.lstrip(MARK)
    if indent > indent_stack[-1]:
        result += "<ul><li>" + body
        indent_stack.append(indent)
    else:
        while indent < indent_stack[-1]:
            result += "</li></ul>"
            indent_stack.pop()
        result += "</li><li>" + body

while -1 < indent_stack[-1]:
    result += "</li></ul>"
    indent_stack.pop()
        
print result

すこしPerlっぽく書いたのが下。(部分文字列より正規表現を選んだあたりがPerlっぽいつもり)

import re
pat = re.compile("^(\s*)(-?)(.*)")
indent_stack = [-1]
result = ""

def closeTags(indent):
    global result
    while indent < indent_stack[-1]:
        result += "</li></ul>"
        indent_stack.pop()
    
for line in data.split("\n"):
    (_1, _2, _3) = pat.match(line).groups()
    if _3 == "":
        continue
    if _2 == "":
        result += _3
        continue

    indent = len(_1)
    if indent > indent_stack[-1]:
        result += "<ul><li>" + _3
        indent_stack.append(indent)
    else:
        closeTags(indent)
        result += "</li><li>" + _3

closeTags(-1)
        
print result

トラックバック(Trackback)

Trackback URL: http://www.nishiohirokazu.org/mt/mt-tb.cgi/76

ご意見・ご感想をお送りください(フィードバック)

(フィードバックはメールで送信され、基本的に表示されませんが、内容によっては公開させていただくこともございます。ご了承ください。Your comment doesn't appear the page immediately. If the comment has value to other people, it will be put on the page or subsequent entries. Thank you.)

上の情報は、いずれも未記入でかまいません。 All of above questions are optional.