31 Stimmen

Schneller Weg, um illegale xml Unicode-Zeichen in Python zu filtern?

El XML-Spezifikation listet eine Reihe von Unicode-Zeichen auf, die entweder illegal oder "entmutigt" sind. Wie kann ich aus einer Zeichenkette alle illegalen Zeichen entfernen?

Ich habe mir den folgenden regulären Ausdruck ausgedacht, aber er ist ein bisschen langatmig.

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 kennt keine Unicode-Zeichen über 0xFFFF, so dass diese nicht gefiltert werden müssen).

22voto

Olemis Lang Punkte 718

Vor kurzem haben wir (Trac XmlRpcPlugin Maintainer) wurden darüber informiert, dass der obige reguläre Ausdruck Surrogatpaare auf Python Narrow Builds entfernt (siehe th:kommentar:13:ticket:11050 ) . Ein alternativer Ansatz besteht in der Verwendung der folgenden Regex (siehe 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. Siehe dieser Beitrag über Leihmütter zu erklären, wozu sie da sind.

Update damit sie nicht übereinstimmen (ersetzen) 0x0D die eine gültiges XML-Zeichen .

9voto

Megacier Punkte 305

Hier ist eine aktualisierte Version von Olemis Langs Antwort für Python 3:

import re
import sys

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 = [fr'{chr(low)}-{chr(high)}' for (low, high) in illegal_unichrs]
xml_illegal_character_regex = '[' + ''.join(illegal_ranges) + ']'
illegal_xml_chars_re = re.compile(xml_illegal_character_regex)
# filtered_string = illegal_xml_chars_re.sub('', original_string)

4voto

u0b34a0f6ae Punkte 45029

Sie können auch die translate-Methode von Unicode verwenden, um ausgewählte Codepunkte zu löschen. Allerdings ist die Zuordnung, die Sie haben, ziemlich groß (2128 Codepunkte) und das könnte es viel langsamer als nur mit einer Regex machen:

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)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X