2010-07-22 20 views
17

Jak usunąć białe spacje i znaki końca linii w łańcuchu XML w Pythonie 2.6? Próbowałem następujące pakiety:Usuwanie białych znaków w ciągu znaków XML

etree: Ten fragment Zachowuje oryginalne spacje:

xmlStr = '''<root> 
    <head></head> 
    <content></content> 
</root>''' 

xmlElement = xml.etree.ElementTree.XML(xmlStr) 
xmlStr = xml.etree.ElementTree.tostring(xmlElement, 'UTF-8') 
print xmlStr 

nie mogę użyć Pythona 2.7, które zapewniają parametr method.

minidom: tak samo:

xmlDocument = xml.dom.minidom.parseString(xmlStr) 
xmlStr = xmlDocument.toprettyxml(indent='', newl='', encoding='UTF-8') 
+0

to może pomóc stosując lxml usunąć wszystkie puste linie i biało-spacje z węzła tekstowego http://stackoverflow.com/a/19396130/973699 – DevC

Odpowiedz

29

Najprostszym rozwiązaniem jest prawdopodobnie za pomocą lxml, w którym można ustawić opcję parsera ignorować spacje między elementami:

>>> from lxml import etree 
>>> parser = etree.XMLParser(remove_blank_text=True) 
>>> xml_str = '''<root> 
>>>  <head></head> 
>>>  <content></content> 
>>> </root>''' 
>>> elem = etree.XML(xml_str, parser=parser) 
>>> print etree.tostring(elem) 
<root><head/><content/></root> 

Prawdopodobnie będzie to wystarczające dla Twoich potrzeb, ale niektóre ostrzeżenia, aby na wszelki wypadek strona:

To będzie po prostu usunąć whitespace węzłów między elementami, a nie próbować usunąć whitespace węzły wewnątrz elementów z zawartością mieszaną:

>>> elem = etree.XML('<p> spam <a>ham</a> <a>eggs</a></p>', parser=parser) 
>>> print etree.tostring(elem) 
<p> spam <a>ham</a> <a>eggs</a></p> 

Początkowe lub końcowe białe spacje ze znaków tekstowych nie zostaną usunięte. Będzie jednak nadal w pewnych okolicznościach usuwać białe spacje z mieszanych treści: jeśli parser nie natrafił jeszcze na węzły inne niż białe na tym poziomie.

>>> elem = etree.XML('<p><a> ham</a> <a>eggs</a></p>', parser=parser) 
>>> print etree.tostring(elem) 
<p><a> ham</a><a>eggs</a></p> 

Jeśli użytkownik nie chce, że można użyć xml:space="preserve", które będą przestrzegane. Inną opcją byłoby użycie dtd i użycie etree.XMLParser(load_dtd=True), gdzie parser użyje dtd do określenia, które białe znaki są znaczące.

Poza tym, trzeba będzie napisać własny kod, aby usunąć spacje nie chcesz (iteracji potomków, aw stosownych przypadkach, ustaw .text i .tail właściwości zawierające tylko białe znaki do None lub pusty ciąg)

+0

Znalazłem, że jak wskazano przez @Steven, niektóre elementy zawierające tylko białe znaki nie są czyszczone. Użyłem regex, aby to zrobić po wywołaniu 'etree.tostring':' re.sub (r '> \ s + <', '><', xml_str) ' – Rodrigue

+0

Proszę zastąpić' etree.XML (xml_str, parser = p) ' z 'etree.XML (xml_str, parser = parser)' w pierwszym fragmencie. –

-3
xmlStr = ' '.join(xmlStr.split())) 

Stawia to cały tekst w jednej linii zastępującej wielokrotne spacje z pojedynczego wykroju.

xmlStr = ''.join(xmlStr.split())) 

To usunąć całkowicie przestrzeń, w tym przestrzenie wewnątrz tekstu i nie może być używany.

Pierwsza forma może być używany z ryzykiem (ale to poprosić), na wejściu dałeś:

xmlStr = '''<root> 
    <head></head> 
    <content></content> 
</root>''' 
xmlStr = ' '.join(xmlStr.split()) 
print xmlStr 
""" Output: 
<root> <head></head> <content></content> </root> 
""" 

Byłoby to ważne xml. Musiałby być jednak sprawdzony z jakimś rodzajem sprawdzania xml. Czy na pewno chcesz XML? Czytałeś artykuł: Python Is Not Java

+0

-1 Twoja sugestia zniszczy coś takiego jak "" " \ t \ tfoo" "" –

+0

Muszę się zgodzić z Johnem. Nie zachowuje to w ogóle składni XML. – mattbasta

6

Biała spacja jest znacząca w dokumencie XML. Używanie białych znaków do wcięcia jest kiepskim użyciem XML, ponieważ wprowadza znaczące dane tam, gdzie ich naprawdę nie ma - i niestety, jest to normą. Jakiekolwiek programistyczne podejście do usuwania pustych przestrzeni będzie w najlepszym razie domysły - potrzebujesz lepszej wiedzy na temat tego, co przekazuje XML, aby właściwie usunąć białe spacje, bez wchodzenia na niektóre palce danych.

-1

trochę nieporadny rozwiązanie bez lxml :-)

data = """<root> 

    <head></head> <content></content> 

</root>""" 

data3 = [] 
data2 = data.split('\n') 
for x in data2: 
    y = x.strip() 
    if y: data3.append(y) 
data4 = ''.join(data3) 
data5 = data4.replace(" ","").replace("> <","><") 

print data5 

Output: <root><head></head><content></content></root> 
-1

Jeśli spacje w „non-węzłów liściowych” jest to, co staramy się usunąć wówczas następująca funkcja to zrobi (rekurencyjnie jeśli określona):

from xml.dom import Node 

def stripNode(node, recurse=False): 
    nodesToRemove = [] 
    nodeToBeStripped = False 

    for childNode in node.childNodes: 
     # list empty text nodes (to remove if any should be) 
     if (childNode.nodeType == Node.TEXT_NODE and childNode.nodeValue.strip() == ""): 
      nodesToRemove.append(childNode) 

     # only remove empty text nodes if not a leaf node (i.e. a child element exists) 
     if childNode.nodeType == Node.ELEMENT_NODE: 
      nodeToBeStripped = True 

    # remove flagged text nodes 
    if nodeToBeStripped: 
     for childNode in nodesToRemove: 
      node.removeChild(childNode) 

    # recurse if specified 
    if recurse: 
     for childNode in node.childNodes: 
      stripNode(childNode, True) 

Jednak Thanatos ma rację. Białe spacje mogą reprezentować dane w formacie XML, więc należy ich używać z rozwagą.

17

Oto coś szybko wpadłem, bo nie chcą używać lxml:

from xml.dom import minidom 
from xml.dom.minidom import Node 

def remove_blanks(node): 
    for x in node.childNodes: 
     if x.nodeType == Node.TEXT_NODE: 
      if x.nodeValue: 
       x.nodeValue = x.nodeValue.strip() 
     elif x.nodeType == Node.ELEMENT_NODE: 
      remove_blanks(x) 

xml = minidom.parse('file.xml') 
remove_blanks(xml) 
xml.normalize() 
with file('file.xml', 'w') as result: 
    result.write(xml.toprettyxml(indent = ' ')) 

co naprawdę tylko potrzebne, aby ponownie-indent plik XML z inaczej złamaną wcięcia. Nie przestrzega ona dyrektywy preserve, ale, szczerze mówiąc, tak wiele innych programów obsługujących XML, że jest to raczej zabawne wymaganie :) Ponadto, możesz łatwo dodać tego rodzaju funkcjonalność do powyższego kodu (tylko sprawdzić atrybutu space i nie recure jeśli jego wartość jest „zachowanie”.)

+1

Dzięki za to - nie chciałem dodawać lxml do mojego projektu i działało idealnie na moje potrzeby. –

+2

Awesome. Dzięki stary. –

0
xmlStr = xmlDocument.toprettyxml(indent='\t', newl='\n', encoding='UTF-8') 
fix = re.compile(r'((?<=>)(\n[\t]*)(?=[^<\t]))|(?<=[^>\t])(\n[\t]*)(?=<)') 
newXmlStr = re.sub(fix, '', xmlStr) 

z this source

0

Jedyną rzeczą, która przeszkadza mi o toprettyxml xml.dom.minidom'S() jest to, że dodaje puste linie. I nie wydaje się, aby uzyskać komponenty podzielone, więc po prostu napisałem prostą funkcję, aby usunąć puste wiersze:

#!/usr/bin/env python 

import xml.dom.minidom 

# toprettyxml() without the blank lines 
def prettyPrint(x): 
    for line in x.toprettyxml().split('\n'): 
     if not line.strip() == '': 
      print line 

xml_string = "<monty>\n<example>something</example>\n<python>parrot</python>\n</monty>" 

# parse XML 
x = xml.dom.minidom.parseString(xml_string) 

# clean 
prettyPrint(x) 

i to wyjścia kod:

<?xml version="1.0" ?> 
<monty> 
     <example>something</example> 
     <python>parrot</python> 
</monty> 

Jeśli używam toprettyxml() sama w sobie, czyli wydruk (toprettyxml (x)), dodaje niepotrzebnych pustych wierszy:

<?xml version="1.0" ?> 
<monty> 


     <example>something</example> 


     <python>parrot</python> 


</monty> 
Powiązane problemy