2012-04-11 19 views
5

Utilizando el módulo ctypes, puedo importar fácilmente un puntero (c_char) o un tipo c_char_p en python, pero ninguno de estos proporciona una forma de terminar con una cadena de python que contiene bytes de valor cero.¿Cómo se convierte un char * con bytes de 0 valores en una cadena de Python?

c_char_p es cero terminado, lo que significa que una matriz char * de C termina en el primer valor cero.

POINTER (c_char) es la forma recomendada de importar datos binarios que pueden tener 0 valores, pero no parece haber una manera de convertir esto directamente en una cadena de Python.

Puedo hacer esto:

pixels = clibblah.get_pixels() 
a = "" 
for i in range(0, clibblah.get_pixel_length()): 
    a += pixels[i] 

... pero esto 1) no parece muy pythony, y 2) se lleva para siempre (conversión de un bloque de datos de píxeles 640x480 toma unos 2 segundos en mi mac)

He visto un montón de preguntas sobre esto en el desbordamiento de la pila, pero zurcido si puedo ver uno que no dice "¿Por qué tienes que hacer eso?" o "c_char_p hará lo que quieras" (no lo hace, como he descrito anteriormente).

El único consejo creíble que he visto es el uso de la API de C PyString_FromStringAndSize, tal como se recomienda aquí: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/version/Doc/FAQ.html

Realmente no puedo ver cómo eso ayuda embargo, debido a que yo sepa que es una característica Cython, no una pitón uno.

Para los interesados, la razón por la que necesito hacer esto es que estoy trabajando con panda3d y un kinect, y la aplicación kinect c proporciona una matriz de valores char * sin signo y la aplicación panda3d ofrece una llamada setPixels() eso solo toma una cadena de pitón como argumento.

+1

Esto podría ayudar: http://stackoverflow.com/questions/5047536/how-do-i-build-a-python-string-from-a-raw-binary-ctype-buffer –

+0

Se podría también ser útil para dar el código que desea que funcione pero no lo hace. Para que podamos tener una buena idea de cómo necesita/desea que todo se mantenga unido. –

Respuesta

5

Como dijo, use un POINTER(c_char) para obtener un puntero a la matriz de datos binarios. Para poner esto en conjunto en una cadena, sólo puede tomar un trozo de ella, ya que las obras indización de matrices como se esperaba con ctypes punteros:

clibblah = ctypes.cdll.LoadLibrary('clibblah.dylib') 
get_pixels = clibblah.get_pixels 
get_pixels.restype = ctypes.POINTER(ctypes.c_char) 

pixels = get_pixels() 
num_pixels = clibblah.get_pixel_length() 

# Slice the ctypes array into a Python string 
a = pixels[:num_pixels] 
+0

<3 que funciona perfectamente. – Doug

0

No sé cuál es la mejor respuesta a la pregunta principal, pero aquí hay algunos comentarios sobre cómo PyString_FromStringAndSize podría utilizarse para lograr lo que desea.

PyString_FromStringAndSize es parte de la API de Python C: http://docs.python.org/c-api/string.html

Eso significa que se puede usar esto para

  • Escribir un módulo de Python en C/C++ en el que se define un nuevo dato de Python introduce tu C -derived strings-with-null-characters
  • Puede definir ese tipo de datos para que proporcione un constructor Python que acepte argumentos que de alguna manera contengan un puntero a la cadena C en cuestión. Si nada ayuda, el argumento que el constructor acepta podría ser un c_void_p de cytpes.
  • El constructor que defina (en C/C++) tendría que almacenar un puntero al C-string en una variable miembro. También podría hacer una copia y/o aumentar los recuentos de referencia, etc. Como el constructor está escrito en C/C++, todo lo que sea posible allí es posible en ese constructor.

Tendría que compilar esto en una biblioteca .dll/.pyd y luego import en cualquier código de Python.

Es cierto que es un procedimiento bastante complicado. Con suerte, alguien más sugiere una forma más simple, tal vez basada en los tipos directamente.

4

Hay algunos métodos diferentes. Me gusta ctypes.string_at porque no es quisquilloso: funciona independientemente de si proporciona un tipo c_char_p, o un puntero a c_char, o un tipo de puntero de anulación, o incluso solo una dirección int.

s = b'hello\x00world' # create a string containing null bytes 
sz = len(s) 
from ctypes import * 

p = c_char_p(s) # obtain a pointer of various types 
p2 = cast(p,POINTER(c_char)) 
address = cast(p,c_void_p).value 

print p.value # by default it is interpreted as null-terminated 

print p2[:sz] # various methods of explicitly specifying the full length 
print string_at(p,size=sz) 
print (c_char * sz).from_address(address).raw 
+0

BTW, también debería pensar en decodificar la secuencia de bytes en un objeto de cadena, para facilitar caracteres extendidos, etc. – benjimin

Cuestiones relacionadas