【问题标题】:Convert string to list of bits and viceversa将字符串转换为位列表,反之亦然
【发布时间】:2012-05-01 13:10:30
【问题描述】:

我需要将 ASCII 字符串转换为位列表,反之亦然:

str = "Hi" -> [0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1]

[0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1] -> "Hi"

【问题讨论】:

  • 我有一个函数可以对字符串执行一些操作,但要求它以这种格式传递。
  • @Cameron:错失良机……“好吧,我会字节”

标签: python python-3.x


【解决方案1】:

使用库函数有很多方法可以做到这一点。但我偏向于第三方bitarray模块。

>>> import bitarray
>>> ba = bitarray.bitarray()

从字符串转换需要一些仪式。曾几何时,您可以只使用fromstring,但该方法现在已被弃用,因为它必须将字符串隐式编码为字节。为避免不可避免的编码错误,最好将bytes 对象传递给frombytes。从字符串开始时,这意味着您必须明确指定编码 - 无论如何这是一个好习惯。

>>> ba.frombytes('Hi'.encode('utf-8'))
>>> ba
bitarray('0100100001101001')

转换为列表很容易。 (另外,位串对象已经有很多类似列表的函数了。)

>>> l = ba.tolist()
>>> l
[False, True, False, False, True, False, False, False, 
 False, True, True, False, True, False, False, True]

bitstrings 可以从任何可迭代对象中创建:

>>> bitarray.bitarray(l)
bitarray('0100100001101001')

转换回字节或字符串也相对容易:

>>> bitarray.bitarray(l).tobytes().decode('utf-8')
'Hi'

为了纯粹的娱乐:

>>> def s_to_bitlist(s):
...     ords = (ord(c) for c in s)
...     shifts = (7, 6, 5, 4, 3, 2, 1, 0)
...     return [(o >> shift) & 1 for o in ords for shift in shifts]
... 
>>> def bitlist_to_chars(bl):
...     bi = iter(bl)
...     bytes = zip(*(bi,) * 8)
...     shifts = (7, 6, 5, 4, 3, 2, 1, 0)
...     for byte in bytes:
...         yield chr(sum(bit << s for bit, s in zip(byte, shifts)))
... 
>>> def bitlist_to_s(bl):
...     return ''.join(bitlist_to_chars(bl))
... 
>>> s_to_bitlist('Hi')
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
>>> bitlist_to_s(s_to_bitlist('Hi'))
'Hi'

【讨论】:

  • 感谢您的精彩回复,不幸的是我的环境似乎没有此模块可用,因此约翰的回答更适合我的用例。
【解决方案2】:

可能有更快的方法来做到这一点,但不使用额外的模块:

def tobits(s):
    result = []
    for c in s:
        bits = bin(ord(c))[2:]
        bits = '00000000'[len(bits):] + bits
        result.extend([int(b) for b in bits])
    return result

def frombits(bits):
    chars = []
    for b in range(len(bits) / 8):
        byte = bits[b*8:(b+1)*8]
        chars.append(chr(int(''.join([str(bit) for bit in byte]), 2)))
    return ''.join(chars)

【讨论】:

  • 非常感谢,这正是我想要的!
  • 这应该可以工作,而不是对 0 的字符串进行切片。在 [1]:msg = "101" 在 [2]:msg.zfill(8) Out[2]:'00000101'
【解决方案3】:

不知道为什么,但这里有两个只使用内置函数的丑陋单行器:

s = "Hi"
l = map(int, ''.join([bin(ord(i)).lstrip('0b').rjust(8,'0') for i in s]))
s = "".join(chr(int("".join(map(str,l[i:i+8])),2)) for i in range(0,len(l),8))

产量:

>>> l
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
>>> s
'Hi'

在现实世界的代码中,使用structbitarray 模块。

【讨论】:

  • 大声笑,现在我知道为什么我不应该使用“l”作为变量名......我的控制台字体从来没有遇到过这些问题:D
  • 不客气。但请注意,如果我是你,我不会在生产代码中使用这样丑陋的东西;)
【解决方案4】:

你可以使用内置的bytearray:

>>> for i in bytearray('Hi', 'ascii'):
...     print(i)
... 
72
105

>>> bytearray([72, 105]).decode('ascii')
'Hi'

bin() 转换为二进制。

【讨论】:

    【解决方案5】:
    def text_to_bits(text):
        """
        >>> text_to_bits("Hi")
        [0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
        """
        bits = bin(int.from_bytes(text.encode(), 'big'))[2:]
        return list(map(int, bits.zfill(8 * ((len(bits) + 7) // 8))))
    
    def text_from_bits(bits):
        """
        >>> text_from_bits([0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1])
        'Hi'
        """
        n = int(''.join(map(str, bits)), 2)
        return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode()
    

    另请参阅,Convert Binary to ASCII and vice versa (Python)

    【讨论】:

      【解决方案6】:
      def to_bin(string):
          res = ''
          for char in string:
              tmp = bin(ord(char))[2:]
              tmp = '%08d' %int(tmp)
              res += tmp
          return res
      
      def to_str(string):
          res = ''
          for idx in range(len(string)/8):
              tmp = chr(int(string[idx*8:(idx+1)*8], 2))
              res += tmp
          return res
      

      这些功能真的很简单。
      它不使用第三方模块。

      【讨论】:

        【解决方案7】:

        一些速度比较。这些中的每一个都使用

        python -m timeit "code"
        

        cat <<-EOF | python -m timeit
            code
        EOF
        

        如果是多行。

        比特到字节

        A:100000000 次循环,最好的 3 次:每个循环 0.00838 微秒

        res = 0
        for idx,x in enumerate([0,0,1,0,1,0,0,1]):
            res |= (x << idx)
        

        B:100000000 次循环,最好的 3 次:每个循环 0.00838 微秒

        int(''.join(map(str, [0,0,1,0,1,0,0,1])), 2)
        

        字节到位

        A:100000000 次循环,最佳 3:每个循环 0.00836 微秒

        [(41 >> x) & 1 for x in range(7, -1, -1)]
        

        B:100000 次循环,最好的 3 次:每个循环 2.07 微秒

        map(int, bin(41)[2:])
        

        【讨论】:

          【解决方案8】:
          import math
          
          class BitList:
              def __init__(self, value):
                  if isinstance(value, str):
                      value = sum([bytearray(value, "utf-8")[-i - 1] << (8*i) for i in range(len(bytearray(value, "utf-8")))])
                  try:
                      self.value = sum([value[-i - 1] << i for i in range(len(value))])
                  except Exception:
                      self.value = value
          
              def __getitem__(self, index):
                  if isinstance(index, slice):
                      if index.step != None and index.step != 1:
                          return list(self)[index]
                      else:
                          start = index.start if index.start else 0
                          stop = index.stop if index.stop != None else len(self)
          
                          return BitList(math.floor((self.value % (2 ** (len(self) - start))) >> (len(self) - stop)))
                  else:
                      return bool(self[index:index + 1].value)
          
              def __len__(self):
                  return math.ceil(math.log2(self.value + 1))
          
              def __str__(self):
                  return self.value
          
              def __repr__(self):
                  return "BitList(" + str(self.value) + ")"
          
              def __iter__(self):
                  yield from [self[i] for i in range(len(self))]
          

          然后您可以使用数字或列表(数字或布尔值)初始化BitList,然后您可以获取其值,获取位置项,获取切片并将其转换为列表。注意:目前无法设置项目,但当我添加时我会编辑这篇文章。

          我自己做了这个,然后去寻找如何将字符串(或文件)转换为位列表,然后从另一个答案中弄清楚。

          【讨论】:

            【解决方案9】:

            这可能行得通,但如果你问 PEP 8(长线,复杂),它就行不通

            tobits = lambda x: "".join(map(lambda y:'00000000'[len(bin(ord(y))[2:]):]+bin(ord(y))[2:],x))
            frombits = lambda x: ''.join([chr(int(str(y), 2)) for y in [x[y:y+8] for y in range(0,len(x),8)]])
            

            这些函数像普通函数一样使用。

            【讨论】:

              【解决方案10】:

              因为我喜欢生成器,所以我会在这里发布我的版本:

              def bits(s):
                  for c in s:
                      yield from (int(bit) for bit in bin(ord(c))[2:].zfill(8))
              
              
              def from_bits(b):
                  for i in range(0, len(b), 8): 
                      yield chr(int(''.join(str(bit) for bit in b[i:i + 8]), 2)) 
              
              
              print(list(bits('Hi')))
              [0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
              print(''.join(from_bits([0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1])))
              Hi
              

              【讨论】:

                【解决方案11】:

                如果list 中有位,则只需将其转换为str,然后再转换为数字。数字将表现得像一个位串,然后可以应用按位运算。 例如:

                int(str([1,0,0,1]) | int(str([1,0,1,1])
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-05-27
                  • 2013-07-05
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多