2008-10-13 6 views
9

Je ne sais pas exactement comment poser cette question, et je ne suis pas très proche de trouver une réponse, alors j'espère que quelqu'un pourra m'aider.Traiter une chaîne contenant plusieurs encodages de caractères

J'écris une application Python qui se connecte à un hôte distant et reçoit des données d'octets arrières, que je décompresse en utilisant le module struct intégré de Python. Mon problème est avec les chaînes, car elles incluent plusieurs encodages de caractères. Voici un exemple d'une telle chaîne:

«^lCet est un exemple^GString avec plusieurs^encodages Jcharacter »

Lorsque les différents encodage commence et se termine est marquée en utilisant les caractères d'échappement spéciaux:

  • ^L - Latin1
  • ^E - Europe centrale
  • ^T - turc
  • ^B - Baltique
  • ^J - Japonais
  • ^C - cyrillique
  • ^G - grec

Et ainsi de suite ... je besoin d'un moyen de convertir ce genre de chaîne en Unicode, mais je ne suis vraiment pas sûr comment le faire. J'ai lu sur les codecs de Python et string.encode/decode, mais je ne suis pas le plus sage. Je devrais mentionner aussi, que je n'ai aucun contrôle sur la façon dont les chaînes sont produites par l'hôte.

J'espère que quelqu'un peut m'aider avec la façon de commencer à ce sujet.

+0

Votre analyseur lance-t-il une erreur ou vous reste-t-il avec une chaîne Python valide, mais avec des encodages inutilisables? Si oui, les choses peuvent être réparées. Fournir un exemple de chaîne, s'il vous plaît. – DzinX

+0

Je veux dire autre exemple que ci-dessus, car l'exemple ci-dessus n'a que des caractères ASCII. – DzinX

Répondre

4

Il n'y a pas de fonctionnalité intégrée pour décoder une chaîne comme celle-ci, car c'est vraiment son propre codec personnalisé. Vous devez simplement scinder la chaîne sur ces caractères de contrôle et la décoder en conséquence.

est ici un (très lent) exemple d'une telle fonction qui gère latin1 et Shift-JIS:

latin1 = "latin-1" 
japanese = "Shift-JIS" 

control_l = "\x0c" 
control_j = "\n" 

encodingMap = { 
    control_l: latin1, 
    control_j: japanese} 

def funkyDecode(s, initialCodec=latin1): 
    output = u"" 
    accum = "" 
    currentCodec = initialCodec 
    for ch in s: 
     if ch in encodingMap: 
      output += accum.decode(currentCodec) 
      currentCodec = encodingMap[ch] 
      accum = "" 
     else: 
      accum += ch 
    output += accum.decode(currentCodec) 
    return output 

Une version plus rapide peut utiliser str.split, ou des expressions régulières.

(Aussi, comme vous pouvez le voir dans cet exemple, «^J » est le caractère de contrôle pour « saut de ligne », afin que vos données d'entrée va avoir des restrictions intéressantes.)

3

J'écrirais un codec qui a balayé de manière incrémentale la chaîne et a décodé les octets comme ils sont venus. Essentiellement, vous devez séparer les chaînes en morceaux avec un encodage cohérent et les décoder et les ajouter aux chaînes qui les suivent.

1

Je ne pense pas vous avez un moyen de convaincre la personne qui héberge l'autre machine de passer à unicode?

C'est l'une des raisons pour lesquelles Unicode a été inventé, après tout.

+0

Comme je l'ai dit, je n'ai aucun contrôle sur l'hôte lui-même. L'hôte est en fait un jeu d'ordinateur auquel mon application se connecte, et je crois que c'est ainsi qu'il gère son rendu de texte en interne. –

2

Vous devez absolument diviser la chaîne en premier dans les sous-chaînes avec différents encodages, et les décoder séparément.Juste pour le plaisir, l'obligation « d'une ligne » Version:

import re 

encs = { 
    'L': 'latin1', 
    'G': 'iso8859-7', 
    ... 
} 

decoded = ''.join(substr[2:].decode(encs[substr[1]]) 
      for substr in re.findall('\^[%s][^^]*' % ''.join(encs.keys()), st)) 

(aucune vérification d'erreur, et vous aurez envie de décider comment gérer «^» caractères dans les sous-chaînes)

+0

Vous avez fait exactement la même erreur que moi! –

7

est relativement ici exemple simple de comment le faire ...

# -*- coding: utf-8 -*- 
import re 

# Test Data 
ENCODING_RAW_DATA = (
    ('latin_1', 'L', u'Hello'),  # Latin 1 
    ('iso8859_2', 'E', u'dobrý večer'), # Central Europe 
    ('iso8859_9', 'T', u'İyi akşamlar'), # Turkish 
    ('iso8859_13', 'B', u'Į sveikatą!'), # Baltic 
    ('shift_jis', 'J', u'今日は'),  # Japanese 
    ('iso8859_5', 'C', u'Здравствуйте'), # Cyrillic 
    ('iso8859_7', 'G', u'Γειά σου'), # Greek 
) 

CODE_TO_ENCODING = dict([(chr(ord(code)-64), encoding) for encoding, code, text in ENCODING_RAW_DATA]) 
EXPECTED_RESULT = u''.join([line[2] for line in ENCODING_RAW_DATA]) 
ENCODED_DATA = ''.join([chr(ord(code)-64) + text.encode(encoding) for encoding, code, text in ENCODING_RAW_DATA]) 

FIND_RE = re.compile('[\x00-\x1A][^\x00-\x1A]*') 

def decode_single(bytes): 
    return bytes[1:].decode(CODE_TO_ENCODING[bytes[0]]) 

result = u''.join([decode_single(bytes) for bytes in FIND_RE.findall(ENCODED_DATA)]) 

assert result==EXPECTED_RESULT, u"Expected %s, but got %s" % (EXPECTED_RESULT, result) 
Questions connexes