2009-11-10 81 views
23

XML specification列出了一串非法或“不鼓励”的Unicode字符。给定一个字符串,我如何从中删除所有非法字符?在python中过滤非法的xml unicode字符的快速方法?

我想出了下面的正则表达式,但它有点儿含义。

illegal_xml_re = re.compile(u'[\x00-\x08\x0b-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff\ufdd0-\ufddf\ufffe-\uffff]') 
clean = illegal_xml_re.sub('', dirty) 

(Python 2.5的不知道的Unicode字符以上0xFFFF的,所以无需过滤那些。)

+0

Python的最大unicode代码点取决于它在编译时的配置方式,请检查sys.maxunicode。 – u0b34a0f6ae 2009-11-10 13:18:26

+0

你说得对。我想这更加复杂。 – itsadok 2009-11-10 13:30:05

+2

在我的机器上,使用这个正则表达式来处理一个2.3mb的字符串需要0.34秒。这对我来说似乎相当快。 – 2009-11-10 21:12:17

回答

11

最近我们(Trac的XmlRpcPlugin维护者)已经被告知的事实,正则表达式在Python狭窄的版本上带上代替对(参见th:comment:13:ticket:11050)。另一种方法是使用以下正则表达式(请参见th:changeset:13729)。

_illegal_unichrs = [(0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x1F), 
         (0x7F, 0x84), (0x86, 0x9F), 
         (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF)] 
if sys.maxunicode >= 0x10000: # not narrow build 
     _illegal_unichrs.extend([(0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), 
           (0x3FFFE, 0x3FFFF), (0x4FFFE, 0x4FFFF), 
           (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), 
           (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), 
           (0x9FFFE, 0x9FFFF), (0xAFFFE, 0xAFFFF), 
           (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), 
           (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), 
           (0xFFFFE, 0xFFFFF), (0x10FFFE, 0x10FFFF)]) 

_illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) 
        for (low, high) in _illegal_unichrs] 
_illegal_xml_chars_RE = re.compile(u'[%s]' % u''.join(_illegal_ranges)) 

p.s.见this post on surrogates解释他们是什么。

更新以便不匹配(替换)0x0D这是一个valid XML character

+0

请注意,代理对明确地从W3C XML规范中的合法字符中排除,因此任何包含它们的xml都不能保证在其他库中正确解析。但是,由于通常您会将XML序列化为utf-8或utf-16,因此问题应该消失。只要避开utf-32即可。 – itsadok 2014-03-09 06:56:01

+0

我已更新正则表达式以匹配0x0D字符。参见[th:ticket:11635](http://trac-hacks.org/ticket/11635),[th:changeset:13776](http://trac-hacks.org/changeset/13776)和[XML character范围定义](http://www.w3.org/TR/REC-xml/#NT-Char)。 – 2014-03-19 04:53:59

+0

好点。我也更新了我的版本。 – itsadok 2014-03-19 07:24:30

3

你也可以使用unicode的翻译方法来删除选定的代码点。但是,你有映射是相当大的(2128码点),并且可能使其远远超过只使用一个正则表达式慢:

ranges = [(0, 8), (0xb, 0x1f), (0x7f, 0x84), (0x86, 0x9f), (0xd800, 0xdfff), (0xfdd0, 0xfddf), (0xfffe, 0xffff)] 
# fromkeys creates the wanted (codepoint -> None) mapping 
nukemap = dict.fromkeys(r for start, end in ranges for r in range(start, end+1)) 
clean = dirty.translate(nukemap) 
+1

经过一些测试后,这似乎比正则表达式慢得多,特别是对于大型字符串。 – itsadok 2009-11-10 14:57:47