Python食谱-1.23.Unicode数据编码输出到XML或HTML文件

原文作者:David Goodger, Peter Cogolo
中文翻译:Tony (digitalsatori)

问题

要将Uicode文本输出到HTML或是XML应用程序,且只能用较为流行但受限的编码比如:ASCII码,或Latin-1,如何操作?

解决方法

Python 有一个叫 xmlcharrefreplace 的编码出错处理器,它可以将选定编码之外的字符替换为XML数字字符参考(XML numeric characters references):

def encode_for_xml(unicode_data, encoding='ascii'):
    return unicode_data.encode(encoding, 'xmlcharrefreplace')

上述的方法也可以用来处理HTML输出,但是我们希望能用HTML的‘符号实体‘(symbolic entity)。这样的话,我们就要自己定义一个函数并将其注册为编码错误处理函数。弄这么个函数其实并不难,因为Python标准库中已经有一个叫 htmlentitydefs 的模块,其包含了所有的HTML的实体定义。

import codecs
from htmlentitydefs import codepoint2name
def html_replace(exc):
    if isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)):
        s = [ u'&%s;' % codepoint2name[ord(c)]
              for c in exc.object[exc.start:exc.end] ]
        return ''.join(s), exc.end
    else:
        raise TypeError("can't handle %s" % exc._ _name_ _)
codecs.register_error('html_replace', html_replace)

这个错误处理函数注册好之后,我们可以用一个函数来将它封装起来:

def encode_for_html(unicode_data, encoding='ascii'):
    return unicode_data.encode(encoding, 'html_replace')

讨论

现在用下面的代码来测试一下(将之与上面的函数放在同一个Python文件中):

if _ _name_ _ == '_ _main_ _':
    # demo
    data = u'''\
<html>
<head>

</head>
<body>
<p>accented characters:
<ul>
<li>\xe0 (a + grave)
<li>\xe7 (c + cedilla)
<li>\xe9 (e + acute)
</ul>
<p>symbols:
<ul>
<li>\xa3 (British pound)
<li>\u20ac (Euro)
<li>\u221e (infinity)
</ul>
</body></html>
'''
    print encode_for_xml(data)
    print encode_for_html(data)

运行这个文件,可以看到如下结果:

<li>&#224; (a + grave)
<li>&#231; (c + cedilla)
<li>&#233; (e + acute)
   ...
<li>&#163; (British pound)
<li>&#8364; (Euro)
<li>&#8734; (infinity)
   ...
<li>&agrave; (a + grave)
<li>&ccedil; (c + cedilla)
<li>&eacute; (e + acute)
   ...
<li>&pound; (British pound)
<li>&euro; (Euro)
<li>&infin; (infinity)

encode_for_xml 比较通用, encode_for_html 产生的结果的可读性较强。两个函数产生的结果放到浏览器里看是完全一样的。你可以运行以上这个Python文件并将结果重定向到一个html文件,然后用浏览器打开查看。

必须要记得Unicode数据在打印或写入文件前一定要做编码处理。UTF-8是一个不错的字符编码,因为他能处理所有的Unicode字符。但是目前还有很多用户和程序在使用ASCII码或Latin-1。如果Unicode数据中有超出指定编码范围的字符(比如本例中的带重音的字符和所有的符号都无法用ASCII编码,而无限符号(infinity)无法用Latin-1编码),指定的编码无法自己处理这些数据。Python提供了一个称为 xmlcharrefreplace 错误处理函数,它可以将无法编码的字符用XML数字字符参考来替代,比如: &#8734; 表示无限符号。在本配方中展示了如何来编写和注册另一个近似的错误处理函数 html_replace ,这个函数主要用于处理HTML输出。 html_replace 将无法编码的字符用更加可读的HTML符号实体来替代,比如用 &infin; 来表示无限符号。 html_replace 不是很通用,因为它并不能处理所有的Unicode字符,而且也不能用于非HTML的应用环境。

上述的错误处理函数对于除XML或HTML以外的输出是没有意义的。比如TeX或其它标记语言并不能识别XML数字字符参考。当然,如果你知道如何为这些标记语言创建相应的符号参考,你就可以用本配方中的方法来创建和注册相应的错误处理函数。

另外,Python标准库中的 codecs 模块提供了一个有效的方法,将Unicode数据编码输出到文件,并根据设定进行错误处理:

outfile = codecs.open('out.html', mode='w', encoding='ascii',
                       errors='html_replace')

现在我们可以用 outfile.write(unicode_data) 将任意Unicode字符串 unicode_data 输出到指定的文件,编码和错误处理会自动进行。当输出结束后,我们需要调用 outfile.close()

参见

Python库参考手册和Python in a Nutshelll中关于 codecs 模块和 htmlentitydefs 的介绍

Leave a Response