如何将整数位掩码解码为原始输入字符

时间:2019-08-02 21:14:16

标签: python bitmask

我正在从将可选输入字符标志存储为打包整数(使用二进制打包)的应用程序中读取数据。为了便于讨论,请使用以下示例:A=1, B=2, C=4, D=8, E=16
当用户输入:'AC'时,存储的值为5 (=1+4)
当用户输入:'ABCD'时,存储的值为15 (=1+2+4+8)
我想从整数值(5 => 'AC'15 => 'ABCD')中恢复原始输入。大小写和顺序并不重要。

这是我的新手,因此请阅读位掩码和按位操作并编写一些工作代码。基本上,我将整数转换为格式为0 / 1s字符串的二进制值。然后,我检查每个字符串位置(位)是否为True / False(1/0)。当为True时,我将匹配字符添加到另一个字符串(来自输入选项的有序字符串)。

我的直觉告诉我,有一种更简单的方法可以做到这一点;在单个操作中将二进制表示形式用作检查字符串上的“掩码”。我发现了几篇关于位屏蔽的文章,但没有回答我的问题:
Checking specific bits of a bitmask
How to take bit number 3 from 8 bits

是否可以简化下面的代码?
它适用于所有i_flag = 1至31的值。
[我知道我需要对无效的i_flag值(=0>(n_char**2)-1)进行错误检查
一旦设置了转换逻辑,我将添加它。]

chk_str = 'EDCBA'
i_flag = 1
str_flag=''
b_flag = ('{:0'+str(len(chk_str))+'b}').format(i_flag)
for pos in range(len(b_flag)) :
   if int(b_flag[pos]):
       str_flag += chk_str[pos]

print ('for int=', i_flag, ',flags are:',str_flag)      

5 个答案:

答案 0 :(得分:2)

您可以使用while循环来将输入整数值不断移位1位,直到变为0为止,并且对于每次迭代,将输出整数附加到映射字符串当前位置的字符(如果最低)整数的位是1:

def convert(i):
    output = ''
    pos = 0
    while i:
        if i & 1:
            output += 'ABCDE'[pos]
        pos += 1
        i >>= 1
    return output

以便convert(5)返回'AC',而convert(15)返回'ABCD'

答案 1 :(得分:2)

处理此问题的一种很酷的方法是创建一个通用的BitMask

import string
import itertools


class BitMask(object):
    STR_TOKENS = string.ascii_letters
    STR_EMPTY = '_'
    STR_FULL = False

    def __init__(
            self,
            value=None,
            ignore=True):
        if isinstance(value, str):
            self.value = self.from_tokens(value, self.STR_TOKENS, ignore)
        else:
            self.value = value

    def __repr__(self):
        return bin(self.value)

    def __iter__(self):
        value = self.value
        while value:
            yield value & 1
            value >>= 1

    def to_tokens(self, tokens, empty, full):
        if full:
            return [
                token if value else empty
                for token, value in
                itertools.zip_longest(tokens, self, fillvalue=False)]
        else:
            return [
                token for token, value in zip(tokens, self) if value]

    def __str__(self):
        return ''.join(
            self.to_tokens(self.STR_TOKENS, self.STR_EMPTY, self.STR_FULL))

    def from_tokens(self, seq, tokens, ignore):
        if tokens is None:
            tokens = self.STR_TOKENS
        valid_tokens = set(tokens)
        value = 0
        for i, item in enumerate(seq):
            if item in valid_tokens:
                value |= 1 << tokens.index(item)
            elif not ignore:
                raise ValueError(f'Invalid input `{item}` at index: {i}.')
        return value

    def __add__(self, other):
        self.value |= other.value
        return self

    def __mul__(self, other):
        self.value &= other.value
        return self

    def __eq__(self, other):
        return type(self) == type(other) and self.value == other.value

您可以根据需要将其子类化,例如:

class MyBitMask(BitMask):
    STR_TOKENS = string.ascii_uppercase
    def __init__(self, value=None, ignore=False):
        super().__init__(value, ignore)


print(str(MyBitMask(5)))
# AC
print(str(MyBitMask(15)))
# ABCD

而且:

repr(MyBitMask('AC'))
# 0b101

MyBitMask('AC') == MyBitMask(5)
# True

MyBitMask('AC') == BitMask(5)  # NOT THE SAME BITMASK CLASS!
# False

从时间上讲,凉爽会加快速度,即(与其他答案中的方法相比):

def convert(value, tokens=string.ascii_uppercase):
    output = ''
    i = 0
    while value:
        if value & 1:
            output += tokens[i]
        i += 1
        value >>= 1
    return output


def convert2(value, tokens=string.ascii_uppercase):
    return ''.join(tokens[i] for i, c in enumerate(bin(value)[:1:-1]) if c == '1')


def convert3(value, tokens=string.ascii_uppercase):
    result = []
    i = 0
    while value:
        if value & 1:
            result.append(tokens[i])
        i += 1
        value >>= 1
    return ''.join(result)


def convert4(value, tokens=string.ascii_uppercase):
    return ''.join(tokens[pos] for pos in range(value.bit_length()) if value & (1 << pos))


def convert5(value, tokens=string.ascii_uppercase):
    return ''.join(c for b, c in zip(reversed('{:b}'.format(value)), tokens) if b == '1')

print([convert(i) for i in range(16)])
print([convert2(i) for i in range(16)])
print([convert3(i) for i in range(16)])
print([convert4(i) for i in range(16)])
print([convert5(i) for i in range(16)])
print([str(MyBitMask(i)) for i in range(16)])
# ['', 'A', 'B', 'AB', 'C', 'AC', 'BC', 'ABC', 'D', 'AD', 'BD', 'ABD', 'CD', 'ACD', 'BCD', 'ABCD']
%timeit [convert(i) for i in range(1024)]
%timeit [convert2(i) for i in range(1024)]
%timeit [convert3(i) for i in range(1024)]
%timeit [convert4(i) for i in range(1024)]
%timeit [convert5(i) for i in range(1024)]
%timeit [str(MyBitMask(i)) for i in range(1024)]
1.8 ms ± 5.45 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.11 ms ± 83.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.29 ms ± 331 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.42 ms ± 1.03 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.24 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.48 ms ± 151 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

答案 2 :(得分:1)

将数字转换为二进制数(并忽略前2个字符,即“ 0x”),然后 从字母中选择与设置的位相对应的字符

>>> import string
>>> n = 5
>>> ''.join(string.ascii_uppercase[i] for i,c in enumerate(bin(n)[:1:-1]) if c=='1')
'AC'
>>> 
>>> n = 15
>>> ''.join(string.ascii_uppercase[i] for i,c in enumerate(bin(n)[:1:-1]) if c=='1')
'ABCD'
>>> 
>>> n = 2
>>> ''.join(string.ascii_uppercase[i] for i,c in enumerate(bin(n)[:1:-1]) if c=='1')
'B'

答案 3 :(得分:1)

或者,您可以先使用str.format方法将输入整数值转换为二进制字符串,然后将其反转,然后再使用映射字符串zip将其转换为二进制字符串,以便可以使用生成器表达式来在将其余字符连接到字符串之前,请过滤掉不是1的位:

def convert(i):
    return ''.join(c for b, c in zip(reversed('{:b}'.format(i)), 'ABCDE') if b == '1')

以便convert(5)返回'AC',而convert(15)返回'ABCD'

答案 4 :(得分:1)

您可以在输入整数的位长度上迭代位偏移,并相应地测试每个位:

def convert(i):
    return ''.join('ABCDE'[pos] for pos in range(i.bit_length()) if i & (1 << pos))

以便convert(5)返回'AC',而convert(15)返回'ABCD'