我有数十亿行这样的字符串:1010101110100111100100101在内存中。 我需要将其转换为二进制整数列表。这将需要几分钟,似乎太慢了。 我的代码:
def string2vec(binary_str):
return [int(binary_str[i:i + 8], base=2) for i in range(0, 64, 8)]
result= [ string2vec(l) for l in lines ] # this code is slow
binary_str的长度是64,并且每8个二进制字符变成1个二进制整数。
答案 0 :(得分:4)
编辑:似乎此功能可能内置在python中;看评论。我将留下这个答案,因为它提供了一个用于Python的C库的最小工作示例,该库可以处理数组,而我在网上找不到其他地方。
我同意许多评论,即如果您在内存中有一堆人类可读格式的二进制字符串,则显然出了问题。但是,如果有无法避免的无法控制的原因,则可以尝试使用C编写相关功能。这是一个简单的示例,从以下开始:
include <Python.h>
static PyObject * binary_string(PyObject * self, PyObject * args);
static PyMethodDef PyBinaryString_methods[] =
{
{ "binary_string", binary_string, METH_VARARGS, "binary string" },
{ NULL, NULL, 0, NULL }
};
static struct PyModuleDef PyBinaryString_module =
{
PyModuleDef_HEAD_INIT,
"PyBinaryString",
"Binary String",
-1,
PyBinaryString_methods
};
PyMODINIT_FUNC PyInit_PyBinaryString(void)
{
return PyModule_Create(&PyBinaryString_module);
}
static PyObject * binary_string(PyObject * self, PyObject * args)
{
const char * string;
char buf[8];
if(!PyArg_ParseTuple(args, "s", &string)) { return NULL; }
for(int i = 0; i < 8; i++)
{
buf[i] = 0;
for(int j = 0; j < 8; j++)
{
buf[i] |= (string[8 * i + j] & 1) << (7 - j);
}
}
return PyByteArray_FromStringAndSize(buf, 8);
}
这里,我利用了一个事实,即字符串将仅由ASCII'0'和'1'字符组成,前者的ASCII码为偶数,而后者的ASCII码为奇数。 / p>
在我的系统上,我可以通过以下方式进行编译
cc -fPIC -shared -O3 -I/usr/include/python -o PyBinaryString.so PyBinaryString.c
然后像这样在Python中使用它:
>>> from PyBinaryString import binary_string
>>> binary_string("1111111111111111111111111111111111111111111111111111111100000000")
bytearray(b'\xff\xff\xff\xff\xff\xff\xff\x00')
我不是Python程序员,所以有人也许可以提供一种更好的方式来获取/输入python对象格式的数据。但是,在我的机器上,它的运行速度比本地python版本快一个数量级。
如果您进一步了解内存的布局-如果您知道所有ASCII'0'和'1'字符的字符串都是连续的-您可以让C代码一次转换所有内容,这很可能会进一步加快速度。
答案 1 :(得分:4)
binary_str的长度为64,每8个二进制字符转换为1个二进制整数。
所有的字符串切片和Python循环都很昂贵。使用int(s,2)
将整个二进制字符串转换为整数。然后使用array
将整数作为64位整数进行管理并转换为8位整数。您可以决定是否要对字节使用大端或小端的结果:
import random
import time
import array
ints = [random.randrange(1<<64) for _ in range(1000)] # Make 1000 integers
strs = [f'{n:064b}' for n in ints] # Represent as binary strings
print(f'{ints[0]:016X} {strs[0]}')
start = time.perf_counter()
ints2 = [int(s,2) for s in strs] # convert all the strings to integers
a = array.array('Q',ints) # Store in an array. Q = quadwords (64-bit ints)
a.byteswap() # Optional if you want the opposite endian-ness of your machine.
b = array.array('B') # Another array of bytes
b.frombytes(a.tobytes()) # Populate byte array with the bytes from the quadword array.
print(time.perf_counter() - start)
assert ints == ints2
print([hex(n) for n in b[:8]])
输出:
1E27DFA21406A338 0001111000100111110111111010001000010100000001101010001100111000
0.0005346000000372442
['0x1e', '0x27', '0xdf', '0xa2', '0x14', '0x6', '0xa3', '0x38']
我的机器是低位优先的(大多数是)。它将一千个64位二进制字符串转换为整数,将它们存储在数组中,字节交换它们以表示big-endian,然后将数组的字节重新映射为字节数组...所有这些在我的机器上为534.6微秒。我已经显示了第一个64个字符的字符串及其十六进制表示形式,以及最终结果的前8个字节。如果您确实拥有这些字符串的“十亿”,则每十亿个字符串大约需要9分钟,但不要立即将它们全部读入内存:)
答案 2 :(得分:0)
由于只有2 ^ 8 = 256个可能的值,因此您可以构造一个查找表(以dict的形式),其中包含8个字符的字符串作为键,并作为对应的整数作为值。