Как я могу проанализировать двоичный файл, который, похоже, использует сочетание кодовых точек Юникода и шестнадцатеричных значений для строк?

#python #unicode #encoding #byte

#python #юникод #кодирование #байт

Вопрос:

У меня есть двоичный файл с текстом в нем. Мне нужно захватить шестнадцатеричные байты строки и преобразовать их в читаемый текст. Я использую Python 3.

Кодировка, похоже, UTF-8, но у меня возникли некоторые проблемы с декодированием некоторых конкретных строк. Видите ли, некоторые строки имеют кодовые точки Юникода для представления символов, тогда как другие используют свои шестнадцатеричные значения, соответствующие их записи в таблице символов UTF-8. Вот пример:

 4D 6F 6A 65 20 53 70 6F 72 65 20 76 FD 74 76 6F 72 79 -> Moje Spore výtvory
 

FD Байт в этой строке представляет ý символ, однако FD соответствует этому символу, только если мы проверяем кодовую точку Юникода в таблице символов, как показано здесь:

введите описание изображения здесь

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

 4C 45 47 4F C2 AE -> LEGO®
 

В этом примере два байта C2 AE представляют ® символ. Однако это их шестнадцатеричное представление, а не кодовая точка Юникода, как показано здесь:

введите описание изображения здесь

Теперь вот в чем проблема. Я не могу определить, когда строка использует кодовые точки Юникода, а когда использует шестнадцатеричные значения, и мне нужно, чтобы это было проанализировано идеально. Есть идеи относительно того, почему это может быть так? Python вылетает, если я пытаюсь декодировать это с помощью UTF-8, поскольку, когда он достигает значения like FD , он не знает, что делать. Я пытался декодировать байт за байтом с ord() chr() помощью функций and , но, хотя это предотвращает сбой, многобайтовые символы содержат дополнительные элементы, которые не принадлежат (например, пример LEGO будет выглядеть так : LEGO® ). Данные должны быть проанализированы идеально, потому что они должны использоваться для получения контрольной суммы, поэтому даже невидимые вещи изменят результат. Спасибо.

Комментарии:

1. Как был сгенерирован файл?

2. Не знаю, если вы хотите больше подробностей, файл «appinfo.vdf». Это из Steam (магазин видеоигр). Находится в папке «appcache» внутри каталога установки. В нем есть куча словарей, а некоторые ключи содержат строки, это строки, которые я пытаюсь декодировать. Я сам убедился, что строки действительно правильно распознаются и не собирают мусор, поэтому я не разбираю их неправильно, просто кажется, что происходит что-то странное.

3. Ах, Steam. Удачи с этим 🙂

Ответ №1:

Первая строка (с FD ) не имеет кодировки UTF-8. Скорее всего, это ISO-8859-1 или Windows-1252. Представляющий байт ý соответствует значению кодовой точки Юникода, но он не использует «[U] кодовые точки nicode для представления символов».

Строка LEGO имеет кодировку UTF-8. Если вы взламываете строки из файлов и не имеете спецификации, вам просто нужно угадать. UTF-8 должен следовать определенным правилам для его многобайтовой кодировки, поэтому декодирование, скорее всего, завершится неудачей, если вы сначала попробуете UTF-8, а это не UTF-8. Затем вы можете вернуться к . ISO-8859-1 Последний будет декодировать что угодно, даже если это не та кодировка. В итоге может получиться мусор.

Пример для кодировки UTF-8:

 >>> s='Moje Spore výtvory'.encode('utf8')
>>> s
b'Moje Spore vxc3xbdtvory'
>>> s.hex()
'4d6f6a652053706f72652076c3bd74766f7279'
>>> s.decode('utf8')
'Moje Spore výtvory'
>>> s.decode('iso-8859-1')  # note it works, but garbage
'Moje Spore výtvory'
 

Если строка ISO-8859-1- закодированный:

 >>> s='Moje Spore výtvory'.encode('latin1') # an alias for ISO-8859-1
>>> s.hex()
'4d6f6a652053706f72652076fd74766f7279'
>>> s.decode('utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfd in position 12: invalid start byte
>>> s.decode('latin1')
'Moje Spore výtvory'
 

Комментарии:

1. Понятно… итак, вы предлагаете мне использовать utf-8 и вернуться к iso-8859-1. Хотя это предотвратит сбой, мне нужно посмотреть, позволяет ли это генерировать действительные контрольные суммы. Я проведу быстрый тест и вернусь.

2. @tralph3 Если вы выполняете контрольные суммы, это обычно выполняется в байтах, поэтому нет необходимости декодировать. Просто выполните контрольную сумму для байтов.

3. Да, дело в том, что мне нужно проанализировать словари, преобразовать их в формат VDF, а затем закодировать это в байты, чтобы получить контрольную сумму.

4. Что ж, хотя это не решило мою общую проблему, похоже, это правильное решение для декодирования строк, поскольку теперь все выглядит так, как должно. Я все еще не могу получить правильные контрольные суммы, поэтому проблема должна быть где-то еще, но для объема этого вопроса это решение подходит.