如何将整数位掩码解码为原始输入字符
how to decode integer bitmask to original input characters
我正在从一个将可选输入字符标志存储为压缩整数(使用二进制压缩)的应用程序中读取数据。为了便于讨论,请使用以下示例: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/1 字符串的二进制值。然后我检查 True/False (1/0) 的每个字符串位置(位)。当为 True 时,我将匹配字符添加到另一个字符串(来自输入选项的有序字符串)。
我的直觉告诉我有一种更简单的方法可以做到这一点;在单个操作中使用二进制表示作为校验字符串上的 "mask"。我发现了一些涉及位掩码的帖子,但没有回答我的问题:
是否可以简化下面的代码?
它适用于 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)
您可以使用 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)
returns 'AC'
和 convert(15)
returns 'ABCD'
.
将数字转换为二进制(并忽略前 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'
或者,您可以先使用 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)
returns 'AC'
, convert(15)
returns 'ABCD'
.
您可以在输入整数的位长度上迭代一个位偏移量,并相应地测试每个位:
def convert(i):
return ''.join('ABCDE'[pos] for pos in range(i.bit_length()) if i & (1 << pos))
所以 convert(5)
returns 'AC'
和 convert(15)
returns 'ABCD'
.
处理这个问题的一个很酷的方法是创建一个通用的 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)
我正在从一个将可选输入字符标志存储为压缩整数(使用二进制压缩)的应用程序中读取数据。为了便于讨论,请使用以下示例: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/1 字符串的二进制值。然后我检查 True/False (1/0) 的每个字符串位置(位)。当为 True 时,我将匹配字符添加到另一个字符串(来自输入选项的有序字符串)。
我的直觉告诉我有一种更简单的方法可以做到这一点;在单个操作中使用二进制表示作为校验字符串上的 "mask"。我发现了一些涉及位掩码的帖子,但没有回答我的问题:
是否可以简化下面的代码?
它适用于 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)
您可以使用 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)
returns 'AC'
和 convert(15)
returns 'ABCD'
.
将数字转换为二进制(并忽略前 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'
或者,您可以先使用 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)
returns 'AC'
, convert(15)
returns 'ABCD'
.
您可以在输入整数的位长度上迭代一个位偏移量,并相应地测试每个位:
def convert(i):
return ''.join('ABCDE'[pos] for pos in range(i.bit_length()) if i & (1 << pos))
所以 convert(5)
returns 'AC'
和 convert(15)
returns 'ABCD'
.
处理这个问题的一个很酷的方法是创建一个通用的 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)