gae アプリ 開発メモ

Google App Engine アプリの開発メモ / 言語: python, javascript / ビギナー

HTML用に文字をescapeする

ネット上には


こんな感じで載ってる。

from xml.sax import saxutils

# 文字列をHTML(XML)用にエスケープする
escaped_string = saxutils.escape(string)

# HTML(XML)用にエスケープされた文字列を元に戻す
string = saxutils.unescape(escaped_string)

'&', '<', '>'はエスケープしてくれるけど、改行や空白はエスケープしてくれないのね。
テスト結果を HTML にするには、改行や空白も変換して欲しい。

コード


ということで、2つ書いてみた。
HTMLのエスケープは単なる置換なので、

を試してみた。
結果で後述する実行速度に大きな違いがあったりする。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# file: mylib.py

import re

class HtmlEscapeRe(object):
    pairs = [
        ('&', '&amp;'),
        ('<', '&lt;'),
        ('>', '&gt;'),
        (' ', '&nbsp;'),
        ('\n', '<br>'),
        ('\r', ''),
    ]

    escape_dict = dict(pairs)
    unescape_dict = dict([(s1, s0) for s0, s1 in pairs if s1 != ''])

    escape_pattern = re.compile('|'.join(escape_dict))
    unescape_pattern = re.compile('|'.join(unescape_dict))

    def escape(self, text):
        return self.escape_pattern.sub(lambda match: self.escape_dict[match.group(0)], text)

    def unescape(self, text):
        return self.unescape_pattern.sub(lambda match: self.unescape_dict[match.group(0)], text)

class HtmlEscapeReplace(object):

    pairs = [
        ('\r', ''),
        ('&', '&amp;'),
        ('<', '&lt;'),
        ('>', '&gt;'),
        (' ', '&nbsp;'),
        ('\n', '<br>'),
    ]

    def escape(self, text):
        for pair in self.pairs:
            text = text.replace(pair[0], pair[1])
        return text

    def unescape(self, text):
        for pair in self.pairs:
            if pair[1]:
                text = text.replace(pair[1], pair[0])
        return text

テストコード


速度計測用のテストコード

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# file: test.py

from __future__ import with_statement
import StringIO
import mylib

source = ''

def initialize():
    global source

    buffer = StringIO.StringIO()
    with open('jquery-1.7.1.js') as f:
        for line in f:
            buffer.write(line)

    source = buffer.getvalue()

def get_source():
    return source

def test_replace():
    exchanger = mylib.HtmlEscapeReplace()
    escaped = exchanger.escape(source)
    unescaped = exchanger.unescape(escaped)

def test_re():
    exchanger = mylib.HtmlEscapeRe()
    escaped = exchanger.escape(source)
    unescaped = exchanger.unescape(escaped)

initialize()

速度計測

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import test
import timeit

times = 1000

def main():
    t = timeit.Timer('test.test_replace()', 'import test')
    print 'test_replace() %.5f usec/pass' % (1000000 * t.timeit(times) / times)

    t = timeit.Timer('test.test_re()', 'import test')
    print 'test_re() %.5f usec/pass' % (1000000 * t.timeit(times) / times)

if __name__ == '__main__':
    main()

結果

# 1回目
test_replace() 11,130.03280 usec/pass
test_re()      58,312.19978 usec/pass

# 2回目
test_replace() 11,187.75549 usec/pass
test_re()      58,343.82807 usec/pass

ウチの環境では単に文字列.replace()の方が5倍早い。