Как я должен декодировать байты (используя ASCII), не теряя байтов "мусора", если xmlcharrefreplace и backslashreplace не работают?

У меня есть сетевой ресурс, который возвращает мне данные, которые должны (согласно спецификациям) быть закодированной в ASCII строкой. Но в некоторых редких случаях я получаю мусорные данные.

Один ресурс, например, возвращает b'\xd3PS-90AC', тогда как другой ресурс для одного и того же ключа возвращает b'PS-90AC'

Первое значение содержит строку, отличную от ASCII. Очевидно, нарушение спецификации, но это, к сожалению, вне моего контроля. Никто из нас не уверен на 100%, что это действительно мусор или данные, которые следует сохранить.

Приложение, вызывающее удаленные ресурсы, сохраняет данные в локальной базе данных для ежедневного использования. Я мог бы просто сделать data.decode('ascii', 'replace') или ..., 'ignore'), но потом я потерял бы данные, которые впоследствии могут оказаться полезными.

Мой немедленный рефлекс должен был использовать 'xmlcharrefreplace' или 'backslashreplace' в качестве обработчика ошибок. Просто потому, что это приведет к отображению строки. Но затем я получаю следующую ошибку: TypeError: don't know how to handle UnicodeDecodeError in error callback

Единственный обработчик ошибок, который работал, был surrogateescape, но это, по-видимому, предназначено для имен файлов. С другой стороны, для моих целей и целей это сработало.

Почему 'xmlcharrefreplace' и 'backslashreplace' не работают? Я не понимаю ошибку.

Например, ожидаемое выполнение:

>>> data = b'\xd3PS-90AC'
>>> new_data = data.decode('ascii', 'xmlcharrefreplace')
>>> print(repr(new_data))
'&#d3;PS-90AC'

Это надуманный пример. Моя цель - не потерять никаких данных. Если бы я использовал обработчик ошибок ignore или replace, байт, о котором идет речь, по существу исчезнет, ​​а информация будет потеряна.

Ответ 1

>>> data = b'\xd3PS-90AC'
>>> data.decode('ascii', 'surrogateescape')
'\udcd3PS-90AC'

Он не использует html-объекты, но является достойной отправной точкой. Если этого недостаточно, вам придется зарегистрировать свой собственный обработчик ошибок, используя codecs.register_error Я предполагаю.

Для Python3:

def handler(err):
    start = err.start
    end = err.end
    return ("".join(["&#{0};".format(err.object[i]) for i in range(start,end)]),end)

import codecs
codecs.register_error('xmlcharreffallback', handler)
data = b'\xd3PS-90AC'
data.decode('ascii', 'xmlcharreffallback')

Для Python 2

def handler(err):
    start = err.start
    end = err.end
    return (u"".join([u"&#{0};".format(ord(err.object[i])) for i in range(start,end)]),end)

import codecs
codecs.register_error('xmlcharreffallback', handler)
data = b'\xd3PS-90AC'
data.decode('ascii', 'xmlcharreffallback')

Оба производят:

'ÓPS-90AC'

Ответ 2

Для полноты, хотелось добавить, что с python 3.5, backslashreplace работает для декодирования, поэтому вам больше не нужно добавлять настраиваемый обработчик ошибок.