2008-10-13 15 views
9

No estoy muy seguro de cómo hacer esta pregunta realmente, y no estoy cerca de encontrar una respuesta, así que espero que alguien pueda ayudarme.Tratar con una cadena que contiene codificaciones de caracteres múltiples

Estoy escribiendo una aplicación de Python que se conecta a un host remoto y recibe datos de bytes, que descomprimo utilizando el módulo de estructura integrado de Python. Mi problema es con las cadenas, ya que incluyen codificaciones de caracteres múltiples. He aquí un ejemplo de una cadena como:

"^ L Este es un ejemplo^Gstring con múltiples^codificaciones Jcharacter"

Donde las diferentes aperturas de codificación y termina está marcado usando caracteres de escape especiales:

  • ^L - Latin1
  • ^E - Europa central
  • ^T - turco
  • ^B - Báltico
  • ^J - Japonés
  • ^C - cirílico
  • ^G - Griego

Y así sucesivamente ... necesito una manera de convertir este tipo de cadena en Unicode, pero realmente no estoy Seguro cómo hacerlo. He leído sobre los códecs de Python y string.encode/decode, pero en realidad no soy más inteligente. Debo mencionar también que no tengo control sobre cómo el host genera las cadenas.

Espero que alguien me pueda ayudar con la manera de comenzar con esto.

+0

¿Su analizador arroja algún error o le quedan cadenas de Python válidas, pero con codificaciones inutilizables? Si es así, las cosas se pueden arreglar. Proporcione una cadena de ejemplo, por favor. – DzinX

+0

Me refiero a otro ejemplo que el anterior, ya que el ejemplo anterior solo tiene caracteres ASCII. – DzinX

Respuesta

4

No hay funcionalidad incorporada para decodificar una cadena como esta, ya que es realmente su propio códec personalizado. Simplemente necesita dividir la cadena en esos caracteres de control y decodificarla en consecuencia.

Aquí hay un (muy lento) ejemplo de una función de este tipo que se encarga de latin1 y 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 

Una versión más rápida podría utilizar str.split o expresiones regulares.

(Además, como se puede ver en este ejemplo, "^ J" es el carácter de control para el "salto de línea", para que sus datos de entrada va a tener algunas restricciones interesantes.)

3

Escribía un códec que escaneaba incrementalmente la cadena y decodificaba los bytes a medida que aparecían. Esencialmente, tendrías que separar las cadenas en fragmentos con una codificación consistente y decodificarlas y anexarlas a las cadenas que las siguieron.

1

No creo ¿Tiene alguna forma de convencer a la persona que aloja la otra máquina para que cambie a Unicode?

Esta es una de las razones por las que se inventó Unicode, después de todo.

+0

Como ya he dicho, no tengo control sobre el host en sí. El host es en realidad un juego de computadora con el que se conecta mi aplicación, y creo que así es como maneja internamente su renderizado de texto. –

2

Definitivamente tiene que dividir la cadena primero en las subcadenas con codificaciones diferentes, y descodificar cada una por separado.Sólo por diversión, el obligatorio "una línea" versión:

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)) 

(sin comprobación de errores, y también querrá decidir cómo manejar caracteres '^' en subseries)

+0

¡Has cometido exactamente el mismo error que yo! –

7

Aquí hay una relativamente ejemplo simple de cómo hacerlo ...

# -*- 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) 
Cuestiones relacionadas