2009-11-10 72 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