первоначально опубликовано 1 сентября 2012 г. на https://moret1979.wordpress.com/

Привет. Работая над моим магистерским проектом с другом, мы наткнулись на небольшую загадку. Хранилище, которое мы собирались использовать, было предназначено для хранения только строковых значений. Но мы хотели хранить тройки больших целых чисел, поэтому простое их запись в виде десятичных знаков в строках заняло бы больше места, чем необходимо. Число до (2³²)-1 (т.е. 4294967295) может храниться всего в 32 битах, но при кодировании в UTF-8 оно занимает 80 бит.

Что ж, уменьшение пространства, необходимого для хранения этих троек, наполовину могло бы помочь проекту, поэтому я искал любые инструменты, которые могли бы кодировать числа как читаемые строки. Что-то вроде кодировки Base64, но при этом числа не дополняются настолько сильно, что результирующая строка не меньше десятичного числа. Кроме того, я воспользовался возможностью написать это на Ruby, так как сейчас работаю с ним, и опубликовать свой первый гем.

Десятичные числа — это наш способ представления значений по основанию 10, то есть с использованием 10 символов. Base64, как следует из названия, использует 64 символа. Выбранные символы представляют собой читаемые символы — все 26 букв алфавита в верхнем и нижнем регистре, цифры от 0 до 9, а также + и /. Чтобы представить число, мне нужно было фактически изменить основание числа с 10 на 64. Таким образом, число 0 будет A, 1 будет B, 50 — y, 64 — это БА и так далее.

Немного поэкспериментировав, в основном занимаясь построением строк, я заметил, что один и тот же код можно использовать для любого набора символов. И это была хорошая перспектива: на старой доброй ASCII-таблице действительно 95 читаемых символов. Поэтому вместо того, чтобы использовать только 64 символа Base64, я также оставил набор из 95 символов для еще более эффективного использования пространства.

Код написан, я приступил к задаче настроить его как гем. Это было на самом деле просто, довольно прямолинейно, как в руководстве. Вы храните свой код в папке lib, добавляете gemspec в Gemfile и создаете файл gemspec. После этого вы создаете и регистрируетесь на RubyGems, получаете ключ API, а затем гем-билд, гем-пуш и все, гем опубликован.

Очень сладкий. Теперь всем желающим его использовать достаточно запустить gem install num_coder и запустить любой из примеров, описанных в README проекта. Например, вы можете получить список чисел в миллиардах и закодировать его в одну строку и обратно:

> NumCoder.fixed_encode95 [1234567890,1876543290,6758493021], 5
=> “/.y5M7#c69r|iNl”
> NumCoder.fixed_decode95 “/.y5M7#c69r|iNl”, 5
=> [1234567890, 1876543290, 6758493021]

Каждое число использует пять символов вместо ожидаемых десяти. Представление может быть еще более компактным, если использовать еще больший набор символов для остальной части диапазона UTF-8. Но тогда это было бы не так читабельно, в зависимости от платформы. Сократить пространство вдвое будет достаточно!