Python: обратимое кодирование буквенно-цифровой строки в целое число - PullRequest
0 голосов
/ 22 ноября 2018

Я хочу преобразовать строку (состоящую из буквенно-цифровых символов) в целое число, а затем преобразовать это целое число обратно в строку:

string --> int --> string

Другими словами, я хочупредставлять буквенно-цифровую строку целым числом.

Я нашел рабочее решение, которое я включил в ответ, но я не думаю, что это лучшее решение, и меня интересуют другие идеи / методы.

Пожалуйста, не отмечайте это как дубликат только потому, что много похожих вопросов уже существует, я специально хочу простой способ преобразования строки в целое число и наоборот .

Это должно работать для строк, которые содержат буквенно-цифровые символы, то есть строки, содержащие цифры и буквы.

Ответы [ 3 ]

0 голосов
/ 03 февраля 2019

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

Этот кодировщик использует binascii для создания идентичной целочисленной кодировки для одного вответ от charel-f.Я знаю, что он идентичен, потому что я тщательно его протестировал.

Кредит: этот ответ .

from binascii import hexlify, unhexlify

class BytesIntEncoder:

    @staticmethod
    def encode(b: bytes) -> int:
        return int(hexlify(b), 16) if b != b'' else 0

    @staticmethod
    def decode(i: int) -> int:
        return unhexlify('%x' % i) if i != 0 else b''

Если вы используете Python <3.6, удалите необязательные аннотации типов. </p>

Быстрый тест:

>>> s = 'Test123'
>>> b = s.encode()
>>> b
b'Test123'

>>> BytesIntEncoder.encode(b)
23755444588720691
>>> BytesIntEncoder.decode(_)
b'Test123'
>>> _.decode()
'Test123'
0 голосов
/ 03 февраля 2019

Предполагая, что набор символов является просто буквенно-цифровым, то есть az AZ 0-9, для этого требуется 6 бит на символ.Таким образом, использование 8-битного кодирования байтов теоретически является неэффективным использованием памяти.

Этот ответ преобразует входные байты в последовательность 6-битных целых чисел.Он кодирует эти маленькие целые числа в одно большое целое, используя побитовые операции.То, действительно ли это приводит к реальной эффективности хранения, измеряется sys.getsizeof и более вероятно для больших строк.

Эта реализация настраивает кодировку для выбора набора символов.Например, если вы работали с string.ascii_lowercase (5 бит), а не string.ascii_uppercase + string.digits (6 бит), кодирование было бы соответственно эффективным.

Также включены модульные тесты.

import string


class BytesIntEncoder:

    def __init__(self, chars: bytes = (string.ascii_letters + string.digits).encode()):
        num_chars = len(chars)
        translation = ''.join(chr(i) for i in range(1, num_chars + 1)).encode()
        self._translation_table = bytes.maketrans(chars, translation)
        self._reverse_translation_table = bytes.maketrans(translation, chars)
        self._num_bits_per_char = (num_chars + 1).bit_length()

    def encode(self, chars: bytes) -> int:
        num_bits_per_char = self._num_bits_per_char
        output, bit_idx = 0, 0
        for chr_idx in chars.translate(self._translation_table):
            output |= (chr_idx << bit_idx)
            bit_idx += num_bits_per_char
        return output

    def decode(self, i: int) -> bytes:
        maxint = (2 ** self._num_bits_per_char) - 1
        output = bytes(((i >> offset) & maxint) for offset in range(0, i.bit_length(), self._num_bits_per_char))
        return output.translate(self._reverse_translation_table)


# Test
import itertools
import random
import unittest


class TestBytesIntEncoder(unittest.TestCase):

    chars = string.ascii_letters + string.digits
    encoder = BytesIntEncoder(chars.encode())

    def _test_encoding(self, b_in: bytes):
        i = self.encoder.encode(b_in)
        self.assertIsInstance(i, int)
        b_out = self.encoder.decode(i)
        self.assertIsInstance(b_out, bytes)
        self.assertEqual(b_in, b_out)
        # print(b_in, i)

    def test_thoroughly_with_small_str(self):
        for s_len in range(4):
            for s in itertools.combinations_with_replacement(self.chars, s_len):
                s = ''.join(s)
                b_in = s.encode()
                self._test_encoding(b_in)

    def test_randomly_with_large_str(self):
        for s_len in range(256):
            num_samples = {s_len <= 16: 2 ** s_len,
                           16 < s_len <= 32: s_len ** 2,
                           s_len > 32: s_len * 2,
                           s_len > 64: s_len,
                           s_len > 128: 2}[True]
            # print(s_len, num_samples)
            for _ in range(num_samples):
                b_in = ''.join(random.choices(self.chars, k=s_len)).encode()
                self._test_encoding(b_in)


if __name__ == '__main__':
    unittest.main()

Пример использования:

>>> encoder = BytesIntEncoder()
>>> s = 'Test123'
>>> b = s.encode()
>>> b
b'Test123'

>>> encoder.encode(b)
3908257788270
>>> encoder.decode(_)
b'Test123'
0 голосов
/ 22 ноября 2018

Вот что у меня есть:

строка -> байты

mBytes = m.encode("utf-8")

байт -> int

mInt = int.from_bytes(mBytes, byteorder="big")

int -> байты

mBytes = mInt.to_bytes(((mInt.bit_length() + 7) // 8), byteorder="big")

байт -> строка

m = mBytes.decode("utf-8")

попробуйте:

m = "test123"
mBytes = m.encode("utf-8")
mInt = int.from_bytes(mBytes, byteorder="big")
mBytes2 = mInt.to_bytes(((mInt.bit_length() + 7) // 8), byteorder="big")
m2 = mBytes2.decode("utf-8")
print(m == m2)

Вот идентичная версия многократного использования:

class BytesIntEncoder:

    @staticmethod
    def encode(b: bytes) -> int:
        return int.from_bytes(b, byteorder='big')

    @staticmethod
    def decode(i: int) -> bytes:
        return i.to_bytes(((i.bit_length() + 7) // 8), byteorder='big')

Если вы используете Python <3.6, удалите необязательные аннотации типов. </p>

Test:

>>> s = 'Test123'
>>> b = s.encode()
>>> b
b'Test123'

>>> BytesIntEncoder.encode(b)
23755444588720691
>>> BytesIntEncoder.decode(_)
b'Test123'
>>> _.decode()
'Test123'
...