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 = [ ('&', '&'), ('<', '<'), ('>', '>'), (' ', ' '), ('\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', ''), ('&', '&'), ('<', '<'), ('>', '>'), (' ', ' '), ('\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倍早い。