为了账号安全,请及时绑定邮箱和手机立即绑定

Python:将字母数字字符串可逆地编码为整数

Python:将字母数字字符串可逆地编码为整数

开满天机 2021-08-24 18:11:11
我想将字符串(由字母数字字符组成)转换为整数,然后将此整数转换回字符串:string --> int --> string换句话说,我想用整数表示一个字母数字字符串。我找到了一个可行的解决方案,我将其包含在答案中,但我认为这不是最佳解决方案,而且我对其他想法/方法感兴趣。请不要仅仅因为已经存在很多类似的问题而将其标记为重复,我特别想要一种将字符串转换为整数的简单方法,反之亦然。这应该适用于包含字母数字字符的字符串,即包含数字和字母的字符串。
查看完整描述

3 回答

?
侃侃尔雅

TA贡献1801条经验 获得超16个赞

回想一下,字符串可以编码为字节,然后可以编码为整数。然后可以反转编码以获取字节后跟原始字符串。


此编码器用于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,请删除可选的类型注释。


快速测试:


>>> s = 'Test123'

>>> b = s.encode()

>>> b

b'Test123'


>>> BytesIntEncoder.encode(b)

23755444588720691

>>> BytesIntEncoder.decode(_)

b'Test123'

>>> _.decode()

'Test123'


查看完整回答
反对 回复 2021-08-24
?
慕后森

TA贡献1802条经验 获得超5个赞

假设字符集只是字母数字,即 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'


查看完整回答
反对 回复 2021-08-24
  • 3 回答
  • 0 关注
  • 280 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信