gpt4 book ai didi

python - 在 Python 中将字符串转换为 ctypes.c_ubyte 数组的有效方法

转载 作者:太空狗 更新时间:2023-10-29 20:56:20 27 4
gpt4 key购买 nike

我有一个 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。

性能结果:

根据 eryksunBrian Larsen 的建议,这里是 vbox VM 下使用 Ubuntu 12.04 和 Python 2.7 的基准测试。

  • 方法一使用我的原帖
  • 方法 2 使用 ctype from_buffer_copy
  • 方法 3 使用 ctype cast/POINTER
  • 方法4使用了numpy

结果:

  • 方法 1 耗时 3.87 秒
  • 方法2耗时0.42秒
  • 方法 3 耗时 1.44 秒
  • 方法4耗时8.79秒

代码:

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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com