2014-08-30 2 views
5

У меня есть произвольные двоичные данные. Мне нужно сохранить его в системе, которая ожидает действительный UTF8. Он никогда не будет интерпретироваться как текст, мне просто нужно положить его туда и получить его и восстановить мои двоичные данные.Хранить произвольные двоичные данные в системе, принимающей только действительные UTF8

Очевидно, что base64 будет работать, но я не могу иметь такой высокий уровень инфляции.

Как я могу легко достичь этого в python 2.7?

+0

Вопрос на полях: Python 2 или 3? –

+3

[Что вы намерены делать с этими байтами?] (Http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) –

+0

@SylvainLeroux Это 2,7 –

ответ

4

У вас будет , у вас есть, чтобы выразить свои данные, используя только ASCII-символы. Использование Base64 - наиболее эффективный метод (доступный в стандартной библиотеке Python), чтобы сделать это с точки зрения обеспечения того, чтобы двоичные данные соответствовали печатаемому тексту, который также безопасен для UTF-8. Конечно, для того, чтобы выражать одни и те же данные, требуется 33% пространства, но другие методы принимают больше дополнительное пространство.

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

import zlib 
import base64 

def pack_utf8_safe(data): 
    is_compressed = False 
    compressed = zlib.compress(data) 
    if len(compressed) < (len(data) - 1): 
     data = compressed 
     is_compressed = True 
    base64_encoded = base64.b64encode(data) 
    if is_compressed: 
     base64_encoded = '.' + base64_encoded 
    return base64_encoded 

def unpack_utf8_safe(base64_encoded): 
    decompress = False 
    if base64_encoded.startswith('.'): 
     base64_encoded = base64_encoded[1:] 
     decompress = True 
    data = base64.b64decode(base64_encoded) 
    if decompress: 
     data = zlib.decompress(data) 
    return data 

'.' символ не является частью алфавита Base64, так что я использовал его здесь, чтобы отметить сжатые данные.

Вы можете дополнительно побрить 1 или 2 = отступы заполнения с конца кодированных данных Base64; они могут быть снова добавлены при декодировании (добавьте '=' * (-len(encoded) * 4) до конца), но я не уверен, что это стоит того.

Вы можете добиться дополнительной экономии за счет переключения на Base85 encoding, кодирование ASCII с 4 по 5 для двоичных данных, поэтому накладные расходы 20%. Для Python 2.7 это доступно только во внешней библиотеке (Python 3.4 added it to the base64 library). Вы можете использовать python-mom project в 2.7:

from mom.codec import base85 

и заменить все base64.b64encode() и base64.b64decode() звонки с base85.b85encode() и base85.b85decode() звонков вместо этого.

Если вы 100% уверены, ничего по пути не будет рассматривать ваши данные как текст (возможно, изменяющих разделители строк, или интерпретировать и изменить другие управляющие коды), можно также использовать кодировку Base128, уменьшая накладные расходы до 14,3% (8 символов за каждые 7 байтов). Однако я не могу рекомендовать для вас модуль Python, устанавливаемый для установки; есть GitHub hosted module, но я его не тестировал.

+0

на самом деле, base128 был бы более эффективным, выполняя требование использования только символов ASCII. Доступны библиотеки base128 (например, https://www.npmjs.org/package/base128) – isedev

+0

@isedev: d должны гарантировать, что управляющие символы никогда не будут интерпретироваться нигде в течение всего срока службы закодированных данных. –

+0

@isedev: например, если эти данные когда-либо записываются на диск в системе Windows, где файл открывается в тексте (довольно часто), то все кодовые точки '\ x0a' (например,newlines) будет расширен до CRLF-разделителей (так '\ x0d \ x0a'), катастрофических для двоичных данных. –

0

Вы можете декодировать свои байты как данные 8859-1, которые всегда будут выдавать допустимую строку Юникода. После этого вы можете кодировать его в UTF8:

utf8_data = my_bytes.decode('iso8859-1').encode('utf8') 

В среднем, половина ваших данных будет находиться в диапазоне 0-127, который один байт в UTF8, и половина ваших данных будет находиться в диапазоне 128-255, который составляет два байта в UTF8, поэтому ваш результат будет на 50% больше, чем ваши входные данные.

Если у вас есть какая-либо структура для ваших данных, тогда zlib сжимает ее, как предлагает Martijn, может уменьшить размер.

+1

Кодировка Base64 только расширяет данные на 33%; Я рассматривал использование латинского-1, но base64 просто бьет шансы здесь. –

+0

Хм, я забыл это! Base64 лучше UTF8. :( –

+0

Плюс Latin-1 не имеет кодовых точек 128-159, а коды ASCII 0-31 (или 32 равно) обычно не имеют стандартных символов. – tripleee

0

Если ваше приложение действительно требует, чтобы вы могли представлять 256 различных байтовых значений в графически различимой форме, вам действительно нужно 256 кодов Unicode. Задача решена.

Коды ASCII 33-127 - это нешуточные, коды кода Unicode 160-255 также являются хорошими кандидатами для представления себя, но вы можете исключить некоторые из которых трудно отличить (если вы хотите, чтобы OCR или люди обрабатывали они надежно, аа и т. д. могут быть слишком похожими). Выделите остальную часть из набора кодовых точек, которые могут быть представлены в двух байтах - довольно большой набор, но опять же, многие из них графически неотличимы от других глифов в большинстве визуализаций.

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

+0

К сожалению, это очень похоже на ответ Неда, но он затрагивает некоторые проблемы с его предложением. – tripleee

Смежные вопросы