代码之家  ›  专栏  ›  技术社区  ›  charelf

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

  •  1
  • charelf  · 技术社区  · 6 年前

    我想将字符串(由字母数字字符组成)转换为整数,然后将该整数转换回字符串:

    string --> int --> string

    换句话说,我想用整数来表示字母数字字符串。

    我找到了一个有效的解决方案,我将其包含在答案中,但我认为这不是最好的解决方案,我对其他想法/方法感兴趣。

    请不要因为已经存在很多类似的问题而将其标记为重复,我特别想要一种将字符串转换为整数和 .

    这适用于包含字母数字字符的字符串,即包含数字和字母的字符串。

    3 回复  |  直到 4 年前
        1
  •  6
  •   charelf    6 年前

    以下是我目前掌握的情况:

    字符串-->字节

    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,删除可选类型注释。

    >>> s = 'Test123'
    >>> b = s.encode()
    >>> b
    b'Test123'
    
    >>> BytesIntEncoder.encode(b)
    23755444588720691
    >>> BytesIntEncoder.decode(_)
    b'Test123'
    >>> _.decode()
    'Test123'
    
        2
  •  4
  •   Asclepius    5 年前

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

    binascii 产生 同整数编码 和Charle-f回答中的那个。我相信它是相同的,因为我对它进行了广泛的测试。

    学分: this answer .

    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''
    

    快速测试:

    >>> s = 'Test123'
    >>> b = s.encode()
    >>> b
    b'Test123'
    
    >>> BytesIntEncoder.encode(b)
    23755444588720691
    >>> BytesIntEncoder.decode(_)
    b'Test123'
    >>> _.decode()
    'Test123'
    
        3
  •  2
  •   Asclepius    6 年前

    假设字符集仅为字母数字,即a-z a-z 0-9,则每个字符需要6位。因此,使用8位字节编码在理论上是对内存的低效使用。

    sys.getsizeof ,并且更可能用于较大的字符串。

    string.ascii_lowercase string.ascii_uppercase + string.digits

    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'
    
        4
  •  1
  •   Asclepius    3 年前

    所以我需要一本数字字典, 它可能看起来有点难看,但它的效率很高,因为每个字符(英文字母)正好是2个数字,但它可以传输任何类型的unicode字符

    import json
    
    myDict = {
        "le key": "le Valueue",
        2 : {
            "heya": 1234569,
            "3": 4
        },
        'Α α, Β β, Γ γ' : 'שלום'
    }
    def convertDictToNum(toBeConverted):
        return int(''.join([(lambda c: c if len(c) ==2 else '0'+c )(str(ord(c) - 26)) for c in str(json.dumps(toBeConverted))]))
    
    def loadDictFromNum(toBeDecoded):
        toBeDecoded = str(toBeDecoded)
        return json.loads(''.join([chr(int(toBeDecoded[cut:cut + 2]) + 26) for cut in range(0, len(toBeDecoded), 2)]))
    
    numbersDict = convertDictToNum(myDict)
    print(numbersDict)
    # 9708827506817595083206088....
    recoveredDict = loadDictFromNum(numbersDict)
    print(recoveredDict)
    # {'le key': 'le Valueue', '2': {'heya': 1234569, '3': 4}, 'Α α, Β β, Γ γ': 'שלום'}