2009-11-10 24 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は、0xFFFFで上記のUnicode文字については、それらをフィルタリングするので、必要性を全く知らないしません。)

+0

Pythonの最大Unicodeコードポイントは、コンパイル時にどのように設定されたかによって異なります。 'sys.maxunicode'をチェックしてください。 – u0b34a0f6ae

+0

あなたはそうです。私はそれがさらに複雑だと思う。 – itsadok

+2

私のマシンでは、この正規表現を使用して2.3mbの文字列を処理するのに0.34秒かかります。それは私にとってかなり速いようです。 –

答えて

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

+0

0x0D文字に一致する正規表現を更新しました。 [th:ticket:11635](http://trac-hacks.org/ticket/11635)、[th:changeset:13776](http://trac-hacks.org/changeset/13776)および[XML文字範囲定義](http://www.w3.org/TR/REC-xml/#NT-Char)を参照してください。 –

+0

良い点。私は私のバージョンも更新しました。 – itsadok

3

また、選択したコードポイントを削除する方法を変換ユニコードのを使用することができます。しかし、あなたが持っているマッピングは、(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

関連する問題