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