Эффективное форматирование двоичных файлов в строку (например, base64, но для UTF8/UTF16)?

У меня есть много пакетов двоичных данных размером от 16 до 4096 байт, которые необходимо хранить в базе данных и которые должны быть легко сравнимы как единое целое (например, два пакета данных, только если длина совпадает и все байты совпадают). Строки хороши для этого, но слепое преобразование двоичных данных в строку может вызвать проблемы из-за проблем с кодировкой/повторной интерпретацией символов.

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

Существует ли какой-либо общий метод хранения двоичных данных в допустимой строке Unicode, который позволит повысить эффективность в UTF-16 (и, надеюсь, не будет слишком ужасным в UTF-8)? Кодировка base-32768 будет хранить 240 битов в шестнадцати символах, что займет 32 байта UTF-16 или 48 байтов UTF-8. Для сравнения, кодировка base64 будет использовать 40 символов, что займет 80 байтов UTF-16 или 40 байтов UTF-8. Подход, который был разработан для использования того же пространства в UTF-8 или UTF-16, может хранить 48 битов в трех символах, которые занимают восемь байтов в UTF-8 или UTF-16, таким образом сохраняя 240 битов в 40 байтах любой UTF. -8 или UTF-16.

Существуют ли какие-либо стандарты для чего-либо подобного?


person supercat    schedule 22.10.2010    source источник
comment
Не всем инструментам нравятся капли. По общему признанию, вероятно, не стоит ломать голову над созданием поля данных, чтобы кто-то мог вырезать и вставлять в него данные с помощью SQL Server Explorer, но это может быть удобно. Возможно, не хватает методов передачи данных, которые могут работать с UTF-8 и UTF-16, но не могут обрабатывать двоичные данные, чтобы сделать формат обмена целесообразным, но я подумал, что они могут быть. Конечно, хранить данные base64 в 16-битном наборе символов неудобно.   -  person supercat    schedule 11.12.2010


Ответы (1)


Base32768 делает именно то, что вы хотели. Жаль, что прошло пять лет.

Использование (это JavaScript, хотя перенос модуля base32768 на другой язык программирования в высшей степени практичен):

var base32768 = require("base32768");

var buf = new Buffer("d41d8cd98f00b204e9800998ecf842", "hex"); // 15 bytes

var str = base32768.encode(buf); 
console.log(str); // "迎裶垠⢀䳬Ɇ垙鸂", 8 code points

var buf2 = base32768.decode(str);
console.log(buf.equals(buf2)); // true

Base32768 выбирает 32 768 символов из базовой многоязычной плоскости. Каждый символ занимает 2 байта при представлении в виде UTF-16 или 3 байта при представлении в виде UTF-8, что дает именно те характеристики эффективности, которые вы описали: 240 бит могут быть сохранены в 16 символах, т. е. 32 байта в UTF-16 или 48 байтов в UTF-. 8. (За исключением случайного символа заполнения, аналогичного заполнению = в Base64.)

Это делается путем разделения входных байтов (то есть 8-битных чисел без знака) на 15-битные числа без знака и присвоения каждому полученному 15-битному числу одного из 32 768 символов.

Обратите внимание, что выбранные символы также являются «безопасными» — без пробелов, управляющих символов, комбинированных диакритических знаков или подверженности искажению нормализации.

person qntm    schedule 18.04.2016
comment
Интересный. Поскольку код дает начало блока как символы, а не кодовые точки, я не могу сказать, глядя на него, но мне было интересно, будет ли каждый символ в кодировке занимать ровно три байта в UTF-8. С точки зрения эффективности хранения, кодирование с переменной длиной может иметь преимущества, но кодирование с фиксированным форматом кажется более эффективным для работы. Кроме того, если требуется кодирование переменной длины, было бы лучше использовать одно- и двухбайтовые коды UTF8 для целей, отличных от держателей больших объемов данных (например, в качестве маркеров для повторяющихся разделов данных). - person supercat; 19.04.2016
comment
Теоретически это может ответить на вопрос, но было бы лучше включить здесь основные части ответа для будущих пользователей и предоставить ссылку для справки. Ответы, в которых преобладают ссылки, могут стать недействительными из-за ссылка гниет. - person Mogsdad; 19.04.2016
comment
@supercat Да, все выбранные символы взяты из базовой многоязычной плоскости, поэтому 3 байта UTF-8 или 2 байта UTF-16. - person qntm; 19.04.2016
comment
@Mogsdad Я добавил информацию о том, как работает Base32768. - person qntm; 19.04.2016
comment
@qntm: я думал, что BMP включает все кодовые точки ниже 65536; если это не так, какой термин будет включать такие кодовые точки? Есть идеи, используется ли это кодирование где-либо? Лично я думаю, что использование двух байтов для символов ASCII было глупой идеей (даже текстовые документы на иностранном языке содержат огромное количество содержимого ASCII, такого как теги HTML/XML и т. д.), но добавление этого к накладным расходам Base64 еще более отвратительно. После публикации вышеизложенного я также задавался вопросом, имеет ли смысл иметь кодировку, в которой используется одна кодовая точка в диапазоне 256-кодов ниже 2048, так что... - person supercat; 19.04.2016
comment
... каждый закодированный байт будет состоять из двух байтов текста как для кодировки UTF8, так и для кодировки UTF16, а другой байт будет постоянным для всех 256 значений. Подойдет ли для этой цели любой диапазон из 256 кодовых точек ниже 2048? - person supercat; 19.04.2016
comment
@qntm Лучше, но вы можете прочитать Как я могу сделать ссылку на внешний ресурс удобным для сообщества способом. У вас есть описание что это такое и что оно делает; чего не хватает, так это как использовать его для решения конкретной проблемы. - person Mogsdad; 19.04.2016
comment
@supercat Вы правы, базовая многоязычная плоскость - это все кодовые точки от 0 до 65535 включительно. Извините, если что-то из того, что я сказал, подразумевало иное. - person qntm; 19.04.2016
comment
@supercat Это правда, UTF-16 не очень эффективен для текстов с большим количеством ASCII, и у него есть другие недостатки. Однако я понимаю, что это лучше, чем UTF-8 для китайского/японского/корейского текста. - person qntm; 19.04.2016
comment
@supercat Я нигде не знаю, что Base32768 уже используется. Тем не менее, я считаю, что он подходит для использования в ваших целях. - person qntm; 19.04.2016
comment
@supercat Предлагаемая вами альтернативная кодировка, назовем ее Base256, будет иметь эффективность 50% в UTF-8 (у Base64 75%) и эффективность 50% в UTF-16 (у Base32768 94%). В UTF-8 оптимально максимально использовать символы с 1-байтовыми кодировками (т.е. ASCII). В UTF-16 оптимально максимально использовать символы с 2-байтовыми кодировками (т.е. весь BMP). Хорошая мысль, однако. Извините за квадропост, хотел ответить по пунктам. - person qntm; 19.04.2016
comment
@qntm: формат base256 позволит объединять закодированные строки независимо от длины, а его эффективность 50% в UTF16 все равно будет шагом вперед по сравнению с base64. UTF-16 должен был быть более эффективным, чем UTF-8, для текста, в котором используется много кодовых точек в диапазоне 2048-65535, но многие виды документов, независимо от языка, содержат так много разметки ASCII, предназначенной для машинной обработки, что UTF -16 даже здесь не дает большого преимущества. - person supercat; 19.04.2016
comment
@supercat Мне не удалось найти блок из 256 пригодных для использования символов в диапазоне [128, 2048), а также блоки из 128 и только 2 блока из 64. Лучшее, что я могу предложить, это 8 блоков из 32, начиная с кода точки 384, 576, 608, 640, 1184, 1280, 1664 и 1888. Обратите внимание, что все эти CP делятся на 32, что означает, что последние 5 бит входного байта будут такими же, как последние 5 бит кодовой точки . - person qntm; 19.04.2016
comment
@qntm: Что ж, спасибо за попытку. Искали ли вы сопоставления, которые оставляли бы только младшие 8 бит [но позволяли каждому значению байта выбирать независимо из семи или восьми вариантов MSB, которые давали бы кодовые точки 0-2047]? Кодирование потребовало бы использования таблицы поиска, но декодирование могло бы тогда просто отбрасывать старший байт. - person supercat; 19.04.2016
comment
@supercat Отличная идея. Это сработало. Вот ваша таблица поиска: "ԀԁЂԃЄЅІԇЈЉЊЋԌԍԎЏĐđВГДЕЖЗИԙКЛȜȝОПȠȡȢȣȤȥĦħШЩЪЫЬЭЮЯаıвгȴȵȶȷĸȹȺȻȼȽȾȿɀŁłɃɄɅɆɇɈɉŊŋɌɍɎɏɐɑŒœɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥŦŧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟʠʡ¢£¤¥¦Ƨƨ©ƪƫ¬ƭ®ʯ°±ƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃ˄˅ÆˇˈˉˊˋˌˍˎˏÐˑ˒˓˔˕˖×ØϙϚϛϜǝÞßϠϡϢϣǤǥæ˧˨˩˪˫ˬ˭ˮ˯ð˱˲˳˴˵Ƕ÷ø˹˺˻˼˽þ˿" 256 символов. Каждый символ имеет кодовую точку в [128, 2048), а последние 8 бит кодовой точки равны позиции в строке. - person qntm; 19.04.2016
comment
@qntm: кодовая точка 0x0519 отображается в моем браузере как поле, но описывается как кириллический маленький yae. Довольно неприятно, что он не отображается, но если нет проблем с нормализацией, что может быть лучше, чем любой другой код, чей младший байт равен 0x19? - person supercat; 20.04.2016
comment
@supercat Извините, вы никогда не указывали, что отображается в большинстве браузеров как ограничение. С этим ограничением очень сложно работать, потому что оно основано на том, какие шрифты вы установили локально. Вы же не полагаетесь на возможность визуального чтения данных, не так ли? По моему опыту, пока окно отображается, текстовые данные будут сохранены... - person qntm; 20.04.2016
comment
@qntm: Было бы неплохо, если бы это было практично, чтобы что-то появлялось, но это было бы второстепенно по сравнению с другими проблемами. Кажется, что другие символы 0x0_19 либо объединяют метки, объединяют метки, либо отображаются справа налево, поэтому 0x0519 может быть лучшим выбором. Все остальные метки в LTR-скриптах? - person supercat; 20.04.2016