- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个 20 字节的字符串,我想将它转换为 ctypes.c_ubyte
数组以进行位域操作。
import ctypes
str_bytes = '01234567890123456789'
byte_arr = bytearray(str_bytes)
raw_bytes = (ctypes.c_ubyte*20)(*(byte_arr))
有没有办法为了转换而避免从 str 到 bytearray 的深拷贝?
或者,是否可以在没有深拷贝的情况下将字符串转换为字节数组? (使用像 memoryview 这样的技术?)
我正在使用 Python 2.7。
性能结果:
根据 eryksun 和 Brian Larsen 的建议,这里是 vbox VM 下使用 Ubuntu 12.04 和 Python 2.7 的基准测试。
结果:
代码:
import ctypes
import time
import numpy
str_bytes = '01234567890123456789'
def method1():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
byte_arr = bytearray(str_bytes)
result = (ctypes.c_ubyte*20)(*(byte_arr))
t1 = time.clock()
print(t1-t0)
return result
def method2():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
result = (ctypes.c_ubyte * 20).from_buffer_copy(str_bytes)
t1 = time.clock()
print(t1-t0)
return result
def method3():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
result = ctypes.cast(str_bytes, ctypes.POINTER(ctypes.c_ubyte * 20))[0]
t1 = time.clock()
print(t1-t0)
return result
def method4():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
arr = numpy.asarray(str_bytes)
result = arr.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte*len(str_bytes)))
t1 = time.clock()
print(t1-t0)
return result
print(method1())
print(method2())
print(method3())
print(method4())
最佳答案
我不认为那是你想的那样。 bytearray
创建字符串的副本。然后解释器将 bytearray
序列解压成一个 starargs
tuple
并将其合并到另一个新的 tuple
中args(即使在这种情况下没有)。最后,c_ubyte
数组初始值设定项遍历 args tuple
以设置 c_ubyte
数组的元素。仅仅为了初始化数组就需要完成大量工作和大量复制。
相反,您可以使用 from_buffer_copy
方法,假设字符串是具有缓冲区接口(interface)(不是 unicode)的字节串:
import ctypes
str_bytes = '01234567890123456789'
raw_bytes = (ctypes.c_ubyte * 20).from_buffer_copy(str_bytes)
那仍然需要复制字符串,但它只完成一次,而且效率更高。正如评论中所述,Python 字符串是不可变的,可以保留或用作字典键。它的不变性应该得到尊重,即使 ctypes 允许您在实践中违反这一点:
>>> from ctypes import *
>>> s = '01234567890123456789'
>>> b = cast(s, POINTER(c_ubyte * 20))[0]
>>> b[0] = 97
>>> s
'a1234567890123456789'
编辑
我需要强调的是,我不推荐使用 ctypes 来修改不可变的 CPython 字符串。如果必须,那么至少要事先检查 sys.getrefcount
以确保引用计数为 2 或更少(调用加 1)。否则,您最终会对名称(例如 "sys"
)和代码对象常量的字符串实习感到惊讶。 Python 可以自由地重用它认为合适的不可变对象(immutable对象)。如果您跳出语言来改变“不可变”对象,那么您就违反了约定。
例如,如果您修改一个已经散列过的字符串,缓存的散列值对于内容来说不再正确。这打破了它用作 dict 键。具有新内容的另一个字符串和具有原始内容的字符串都不会与字典中的键匹配。前者有不同的哈希值,后者有不同的值。那么获得字典项的唯一方法是使用具有不正确哈希值的变异字符串。继续前面的示例:
>>> s
'a1234567890123456789'
>>> d = {s: 1}
>>> d[s]
1
>>> d['a1234567890123456789']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a1234567890123456789'
>>> d['01234567890123456789']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: '01234567890123456789'
现在考虑一下,如果键是一个在许多地方重复使用的驻留字符串,那会是一团糟。
对于性能分析,通常使用 timeit 模块。在 3.3 之前,timeit.default_timer
因平台而异。在 POSIX 系统上它是 time.time
,在 Windows 上它是 time.clock
。
import timeit
setup = r'''
import ctypes, numpy
str_bytes = '01234567890123456789'
arr_t = ctypes.c_ubyte * 20
'''
methods = [
'arr_t(*bytearray(str_bytes))',
'arr_t.from_buffer_copy(str_bytes)',
'ctypes.cast(str_bytes, ctypes.POINTER(arr_t))[0]',
'numpy.asarray(str_bytes).ctypes.data_as('
'ctypes.POINTER(arr_t))[0]',
]
test = lambda m: min(timeit.repeat(m, setup))
>>> tabs = [test(m) for m in methods]
>>> trel = [t / tabs[0] for t in tabs]
>>> trel
[1.0, 0.060573711879182784, 0.261847116395079, 1.5389279092185282]
关于python - 在 Python 中将字符串转换为 ctypes.c_ubyte 数组的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21483482/
我有一个 20 字节的字符串,我想将它转换为 ctypes.c_ubyte 数组以进行位域操作。 import ctypes str_bytes = '01234567890123456789'
如何将一串以冒号分隔的十六进制数转换为 c_ubyte 的 ctypes 数组?根据ctypes docs ,我可以转换一个硬编码的集合,像这样: >>> from ctypes import * >
如何在 Python 中的 bytes 和 POINTER(c_ubyte) 之间进行转换? 我想将 bytes 对象作为 POINTER(c_ubyte) 参数传递给 C 函数,并且我想使用返回的
当我用 c_char 数组定义一个 ctypes 结构作为字段时,我无法获得该字段的大小,但如果我切换到 c_ubyte 数组,我可以。我的理解是,在下面的示例中,Python 将 bar2 视为指针
我是一名优秀的程序员,十分优秀!