Работа со строкой, содержащей несколько кодировок символов - PullRequest
9 голосов
/ 13 октября 2008

Я не совсем уверен, как на самом деле задать этот вопрос, и я не настолько близок, чтобы найти ответ, поэтому я надеюсь, что кто-то может мне помочь.

Я пишу приложение Python, которое подключается к удаленному хосту и получает обратно байтовые данные, которые я распаковываю с помощью встроенного в Python модуля struct. Моя проблема со строками, так как они включают в себя несколько кодировок символов. Вот пример такой строки:

"^ Это пример ^ Gstring с несколькими кодировками ^ Jcharacter"

Там, где различные кодировки начинаются и заканчиваются, отмечаются специальными escape-символами:

  • ^ L - Latin1
  • ^ E - Центральная Европа
  • ^ T - Турецкий
  • ^ B - Балтика
  • ^ J - японский
  • ^ C - кириллица
  • ^ G - греческий

И так далее ... Мне нужен способ конвертировать такую ​​строку в Unicode, но я действительно не уверен, как это сделать. Я читал о кодеках Python и string.encode / decode, но на самом деле я не самый мудрый. Я должен также упомянуть, что я не могу контролировать, как строки выводятся хостом.

Я надеюсь, что кто-то может помочь мне с этим начать.

Ответы [ 5 ]

7 голосов
/ 13 октября 2008

Вот сравнительно простой пример того, как это сделать ...

# -*- 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)
4 голосов
/ 13 октября 2008

Нет встроенной функциональности для декодирования такой строки, поскольку это действительно собственный кодек. Вам просто нужно разделить строку на эти управляющие символы и декодировать ее соответствующим образом.

Вот (очень медленный) пример такой функции, которая обрабатывает latin1 и 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

Более быстрая версия может использовать str.split или регулярные выражения.

(Также, как вы можете видеть в этом примере, «^ J» является управляющим символом для «новой строки», поэтому ваши входные данные будут иметь некоторые интересные ограничения.)

3 голосов
/ 13 октября 2008

Я бы написал кодек, который постепенно сканировал строку и декодировал байты по мере их поступления. По сути, вам придется разделять строки на куски с единообразной кодировкой, декодировать их и добавлять к последующим цепочкам.

2 голосов
/ 13 октября 2008

Вы определенно должны сначала разбить строку на подстроки с разными кодировками и декодировать каждую отдельно. Просто для удовольствия, обязательная «однострочная» версия:

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

(без проверки ошибок, и вы также захотите решить, как обрабатывать символы '^' в подстроках)

1 голос
/ 13 октября 2008

Не думаю, что у вас есть какой-либо способ убедить человека, на котором установлена ​​другая машина, переключиться на Unicode?

В конце концов, это одна из причин, по которой Unicode был изобретен.

...