Преобразование между ISO-8859-2 и UTF-8 в Python

Мне интересно, как я могу преобразовать символы ISO-8859-2 (latin-2) (я имею в виду целые или шестнадцатеричные значения, представляющие кодированные символы ISO-8859-2) в символы UTF-8.

Что мне нужно сделать с моим проектом на питоне:

  1. Получите шестнадцатеричные значения из последовательного порта, которые представляют собой символы, закодированные в ISO-8859-2.
  2. Расшифруйте их, это - получите от них "стандартные" строки юникода python.
  3. Подготовьте и напишите xml файл.

Использование Python 3.4.3

txt_str = "ąęłóźć"
txt_str.decode('ISO-8859-2')
Traceback (most recent call last): File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'decode'

Основная проблема по-прежнему заключается в подготовке допустимых входных данных для метода «декодирования» (он работает в python 2.7.10, и именно его я использую в этом проекте). Как подготовить допустимую строку из десятичного значения, которое является кодовым номером Latin-2?

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

Пример данных по запросу:

68632057
62206A75
7A647261
B364206F
20616775
777A616E
616A2061
6A65696B
617A20B6
697A7970
6A65B361
70697020
77F36469
62202C79
6E647572
75206A65
7963696C
72656D75
6A616E20
73726F67
206A657A
65647572
77207972
73772065
00000069

Это примерные данные. ISO-8859-2 вставлен в uint32, 4 символа на целое число.

кусок кода, который управляет распаковкой:

l = l[7:].replace(",", "").replace(".", "").replace("\n","").replace("\r","") # crop string from uart, only data left
vl = [l[0:2], l[2:4], l[4:6], l[6:8]] # list of bytes
vl = vl[::-1] # reverse them - now in actual order

Чтобы получить целочисленное значение из шестнадцатеричной строки, я могу просто использовать:

int_vals = [int(hs, 16) for hs in vl]

person user2046193    schedule 02.02.2016    source источник
comment
Это должно быть так же просто, как: this_is_the_text_string.decode('ISO-8859-2'), что дает вам строку юникода (по крайней мере, в Python 3).   -  person elzell    schedule 02.02.2016
comment
Легкий. Преобразование из шестнадцатеричных в байты, декодировать как latin-2, кодировать как UTF-8. У вас есть образцы данных?   -  person Martijn Pieters    schedule 02.02.2016
comment
Однако, если вы собираетесь писать XML, почему бы не сохранить значение в формате Unicode (расшифрованном таким образом из ISO-8859-2) и предоставить библиотеке XML кодировать его в UTF-8?   -  person Martijn Pieters    schedule 02.02.2016
comment
вы должны посмотреть на это. http://stackoverflow.com/questions/26125141/str-object-has-no-attribute-decode-in-python3   -  person bender    schedule 02.02.2016
comment
Строковый тип в Python 3 — Unicode. Если вы хотите ввести необработанные отдельные байты, используйте тип данных строки Python 3 байта; но тогда вам нужно будет кодировать байты как шестнадцатеричные, а не как символы (потому что это символы Unicode).   -  person tripleee    schedule 02.02.2016
comment
Образец данных имеет только четыре символа, которые не являются простым старым 7-битным ASCII; и порядок байтов здесь менее чем прозрачен, так что не совсем понятно, как это вернуть в человекочитаемый текст. Вы могли бы сделать его более полезным и менее громоздким, используя вместо этого что-то вроде b'\xb1\xea\xb3\xf3\xbc\xe6' (и, возможно, информацию о том, что это представление строки "ąęłóźć" в ISO-8859-2); но тогда это уже ответ на ваш вопрос, я думаю.   -  person tripleee    schedule 02.02.2016
comment
Предполагая, что вы читаете данные в Python с помощью PySerial, вы должны получать строку байтов от read(), которая поддерживает метод decode(). Я думаю, что ваш тестовый код не соответствует тому, что вы на самом деле хотите сделать.   -  person Alastair McCormack    schedule 02.02.2016
comment
То, что вы видите, это значения с устройства uart, и они вводятся в мое приложение, ничего не могу с этим поделать. Я до сих пор не знаю, как на самом деле получить символ ISO-8859-2 (который я могу снова декодировать и кодировать в utf-8) из десятичного значения, что является моей главной заботой. @edit Аластер Вы правы, я читаю это с помощью PySerial. Хотя вывод из серийного номера представляет собой шестнадцатеричные целые числа, которые представляют 4 символа ISO-8859-2, поэтому .decode() на read() не будет работать.   -  person user2046193    schedule 02.02.2016
comment
Итак, ваше удаленное устройство UART фактически кодирует в ISO-8859-2, а затем кодирует его в шестнадцатеричное представление ASCII?   -  person Alastair McCormack    schedule 02.02.2016
comment
Или вы говорите, что ваше удаленное устройство кодируется в ISO-8895-2, затем вставляется в uint32, а затем кодируется как шестнадцатеричный ASCII перед подключением к проводу?   -  person Alastair McCormack    schedule 02.02.2016
comment
Мое удаленное устройство uart может печатать только шестнадцатеричные значения, которые находятся в его регистре. Это беспроводное устройство связи. Я использую приложение для Android, чтобы отправить ему строку, и чтобы сделать ее простой и надежной на аппаратной части, я должен использовать кодировку 8b. Более того, мой проект определяет, что я должен использовать кодировку Latin-2+ (например, польские знаки). Я прокомментировал ваш код с деталями.   -  person user2046193    schedule 02.02.2016
comment
Я имею в виду - я реализовал преобразование UTF-16 → ISO-8859-2, поместив четыре из них в один uint32 и отправив его по BT в приложении для Android.   -  person user2046193    schedule 02.02.2016
comment
Голосование против и голосование за закрытие, поскольку фактическая проблема не определена должным образом и слишком специфична   -  person Alastair McCormack    schedule 02.02.2016


Ответы (3)


Ваш пример не работает, потому что вы пытались использовать str для хранения байтов. В Python 3 вы должны использовать byte строк.

На самом деле, если вы используете PySerial, вы все равно будете читать байтовые строки, которые вы можете преобразовать по мере необходимости:

with serial.Serial('/dev/ttyS1', 19200, timeout=1) as ser:
    s = ser.read(10)
    # Py3: s == bytes
    # Py2.x: s == str
    my_unicode_string = s.decode('iso-8859-2')

Если ваши данные iso-8895-2 фактически затем кодируются в шестнадцатеричное представление байтов ASCII, вам необходимо применить дополнительный уровень кодирования:

with serial.Serial('/dev/ttyS1', 19200, timeout=1) as ser:
    hex_repr = ser.read(10)
    # Py3: hex_repr == bytes
    # Py2.x: hex_repr == str

    # Decodes hex representation to bytes
    # Eg. b"A3" = b'\xa3'
    hex_decoded = codecs.decode(hex_repr, "hex") 
    my_unicode_string = hex_decoded.decode('iso-8859-2')

Теперь вы можете передать my_unicode_string в свою любимую XML-библиотеку.

person Alastair McCormack    schedule 02.02.2016
comment
Спасибо за ответ. Фактический ввод, который я получаю от uart, показан в первом сообщении. Я ничего не могу с этим поделать, это только то, что я могу прочитать с моего периферийного устройства UART. Я знаю, что эти символы кодируются следующим образом: 1. Каждый байт ([0:2], [2:4]...) представляет собой шестнадцатеричное число, представляющее символ ISO-8859-2. 2. В каждой строке первый байт является последним (LE/BE). 3. 00 означает, что входная строка не делится на 4. - person user2046193; 02.02.2016
comment
ISO-8859-2 не имеет порядка следования байтов, поскольку каждый символ составляет всего 1 байт, поэтому строка также должна быть закодирована в uint32? Возможно, вы можете поделиться каким-то кодом с удаленной стороны, так как он вообще не понятен - person Alastair McCormack; 02.02.2016
comment
Посмотрите первый пост. К сожалению, я не могу показать вам весь код из-за соглашения об интеллектуальной собственности. - person user2046193; 02.02.2016

Интересные примерные данные. В идеале ваши образцы данных должны быть прямой печатью необработанных данных, полученных от PySerial. Если вы на самом деле получаете необработанные байты в виде 8-значных шестнадцатеричных значений, то:

#!python3
from binascii import unhexlify
data = b''.join(unhexlify(x)[::-1] for x in b'''\
68632057
62206A75
7A647261
B364206F
20616775
777A616E
616A2061
6A65696B
617A20B6
697A7970
6A65B361
70697020
77F36469
62202C79
6E647572
75206A65
7963696C
72656D75
6A616E20
73726F67
206A657A
65647572
77207972
73772065
00000069'''.splitlines())

print(data.decode('iso-8859-2'))

Выход:

W chuj bardzo długa nazwa jakiejś zapyziałej pipidówy, brudnej ulicyumer najgorszej rudery we wsi

Google-переводчик с польского на английский:

The dick very long name some zapyziałej Small Town , dirty ulicyumer worst hovel in the village
person Mark Tolonen    schedule 03.02.2016

Эта тема закрыта. Рабочий код, который обрабатывает то, что нужно сделать:

x=177
x.to_bytes(1, byteorder='big').decode("ISO-8859-2")
person user2046193    schedule 02.02.2016
comment
Как это декодирует данные? Вы получаете байты от PySerial, так как же вы превращаете это в целое число? - person Alastair McCormack; 02.02.2016