2010-06-10 5 views
3

J'essaye d'analyser le fichier XML UTF-8 et de sauvegarder certaines parties dans un autre fichier. Le problème est, que c'est mon premier script Python et je suis totalement confus au sujet des problèmes de codage de caractères que je trouve.Lecture d'un fichier XML UTF-8 et écriture dans un fichier avec Python

Mon script échoue immédiatement quand il tente d'écrire le caractère non-ascii dans un fichier, mais il peut l'imprimer pour commander rapidement (au moins dans un certain niveau)

Voici le XML (des parties qui comptent au moins, il est un fichier * .resx qui contient des chaînes de l'interface utilisateur)

<?xml version="1.0" encoding="utf-8"?> 
<root> 
    <resheader name="foo"> 
      <value>bar</value> 
    </resheader> 
    <data name="lorem" xml:space="preserve"> 
      <value>ipsum öä</value> 
    </data> 
</root> 

Et voici mon script python

from xml.dom.minidom import parse 

names = [] 
values = [] 

def getStrings(path): 
    dom = parse(path) 
    data = dom.getElementsByTagName("data") 

    for i in range(len(data)): 
     name = data[i].getAttribute("name") 
     names.append(name) 
     value = data[i].getElementsByTagName("value") 
     values.append(value[0].firstChild.nodeValue.encode("utf-8")) 

def writeToFile(): 
    with open("uiStrings-fi.py", "w") as f: 
     for i in range(len(names)): 
      line = names[i] + '="'+ values[i] + '"' #varName='varValue' 
      f.write(line) 
      f.write("\n") 

getStrings("ResourceFile.fi-FI.resx") 
writeToFile() 

Et voici le retraçage:

 
Traceback (most recent call last): 
    File "GenerateLanguageFiles.py", line 24, in 
    writeToFile() 
    File "GenerateLanguageFiles.py", line 19, in writeToFile 
    line = names[i] + '="'+ values[i] + '"' #varName='varValue' 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in ran 
ge(128) 

Comment est-ce que je devrais corriger mon manuscrit ainsi il lirait et écrirait correctement les caractères UTF-8? Les fichiers que j'essaie de générer seront utilisés dans l'automatisation des tests avec Robots Framework.

Répondre

6

Vous devrez supprimer l'appel à encode() - à savoir, remplacer nodeValue.encode("utf-8") avec nodeValue - puis changer l'appel à open() à

with open("uiStrings-fi.py", "w", "utf-8") as f: 

Il utilise une version « Unicode-aware » de open() qui vous devrez importer à partir du module codecs, il faut donc ajouter aussi

from codecs import open 

au début du fichier. Le problème est que lorsque vous appeliez nodeValue.encode("utf-8"), vous convertissiez une chaîne Unicode (représentation interne de Python pouvant stocker tous les caractères Unicode) en une chaîne normale (qui ne peut stocker que des caractères codés sur un octet 0-255). Plus tard, lorsque vous construisez la ligne à écrire dans le fichier de sortie, names[i] est toujours une chaîne Unicode mais values[i] est une chaîne régulière. Python essaye de convertir la chaîne normale en Unicode, qui est le type plus général, mais parce que vous ne spécifiez pas de conversion explicite, il utilise le codec ASCII, qui est la valeur par défaut, et ASCII ne peut pas gérer les caractères avec des valeurs d'octets plus grandes. que 127. Malheureusement, plusieurs d'entre eux se produisent dans la chaîne values[i] car le codage UTF-8 utilise fréquemment ces octets de plage supérieure. Alors Python se plaint qu'il voit un personnage qu'il ne peut pas gérer. La solution, comme je l'ai dit plus haut, est de différer la conversion d'Unicode en octets jusqu'au dernier moment possible, et vous le faites en utilisant la version Unicode de open (qui gérera l'encodage pour vous).

Maintenant que j'y pense, au lieu de ce que j'ai dit ci-dessus, une solution alternative serait de remplacer names[i] par names[i].encode("utf-8"). De cette façon, vous convertissez names[i] en une chaîne régulière, et Python n'a aucune raison d'essayer de convertir values[i] en Unicode. Bien, on pourrait argumenter qu'il est bon de garder vos chaînes en tant qu'objets Unicode jusqu'à ce que vous les écriviez dans le fichier ... sinon, je crois que unicode devient la valeur par défaut de Python 3.

0

L'analyseur XML décode le codage UTF-8 de l'entrée quand il lit le fichier et tous les nœuds de texte et attributs du DOM résultant sont alors des objets unicode.Lorsque vous sélectionnez les données intéressantes à partir du DOM, vous ré-encoder le values comme UTF-8, mais vous n'encodez pas le names. Le tableau values résultant contient des chaînes d'octets codées tandis que le tableau names contient toujours des objets Unicode.

Dans la ligne où l'erreur de codage est levée, Python essaie de concaténer un tel nom unicode et une valeur de chaîne d'octet. Pour ce faire, les deux valeurs doivent être du même type et Python essaie de convertir la chaîne d'octets values[i] en unicode, mais il ne sait pas qu'il est codé en UTF-8 et échoue lorsqu'il essaie d'utiliser le codec ASCII.

La meilleure façon de contourner ce serait de garder toutes les chaînes comme objets Unicode et il suffit de les encoder en UTF-8 quand ils sont écrits dans le fichier:

values.append(value[0].firstChild.nodeValue) # encode not yet 
... 
f.write(line.encode('utf-8')) # but now 
Questions connexes