transportation the significado programming programación lenguaje language español swift

the - Convierta una matriz UInt8 de dos bytes a un UInt16 en Swift



swift(lenguaje de programación) (7)

Con Swift quiero convertir bytes de una matriz uint8_t a un entero.

"C" Ejemplo:

char bytes[2] = {0x01, 0x02}; NSData *data = [NSData dataWithBytes:bytes length:2]; NSLog(@"data: %@", data); // data: <0102> uint16_t value2 = *(uint16_t *)data.bytes; NSLog(@"value2: %i", value2); // value2: 513

Intento rápido:

let bytes:[UInt8] = [0x01, 0x02] println("bytes: /(bytes)") // bytes: [1, 2] let data = NSData(bytes: bytes, length: 2) println("data: /(data)") // data: <0102> let integer1 = *data.bytes // This fails let integer2 = *data.bytes as UInt16 // This fails let dataBytePointer = UnsafePointer<UInt16>(data.bytes) let integer3 = dataBytePointer as UInt16 // This fails let integer4 = *dataBytePointer as UInt16 // This fails let integer5 = *dataBytePointer // This fails

¿Cuál es la sintaxis o el código correcto para crear un valor UInt16 a partir de una matriz UInt8 en Swift?

Estoy interesado en la versión NSData y estoy buscando una solución que no use una matriz temporal.


Asumiendo poca codificación endiana.

Para convertir a UInt16 desde [UInt8], puedes hacer algo como

var x: [UInt8] = [0x01, 0x02] var y: UInt16 = 0 y += UInt16(x[1]) << 0o10 y += UInt16(x[0]) << 0o00

Para la conversión a UInt32, este patrón se extiende a

var x: [UInt8] = [0x01, 0x02, 0x03, 0x04] var y: UInt32 = 0 y += UInt32(x[3]) << 0o30 y += UInt32(x[2]) << 0o20 y += UInt32(x[1]) << 0o10 y += UInt32(x[0]) << 0o00

La representación octal de la cantidad de desplazamiento proporciona una buena indicación de cuántos bytes completos se desplazan (8 se convierte en 0o10, 16 se convierte en 0o20, etc.).

Esto se puede reducir a lo siguiente para UInt16:

var x: [UInt8] = [0x01, 0x02] let y: UInt16 = reverse(x).reduce(UInt16(0)) { $0 << 0o10 + UInt16($1) }

y a lo siguiente para UInt32:

var x: [UInt8] = [0x01, 0x02, 0x03, 0x04] let y: UInt32 = reverse(x).reduce(UInt32(0)) { $0 << 0o10 + UInt32($1) }

La versión reducida también funciona para UInt64, y también maneja valores donde la codificación de bytes no usa todos los bytes, como [0x01, 0x02, 0x03]


En Swift 3 o posterior, puede convertir los bytes [UInt8] a Data y obtener el valor de UInt16 utilizando withUnsafeBytes { $0.pointee }

Swift 3 o más tarde

extension Data { var uint16: UInt16 { return withUnsafeBytes { $0.pointee } } }

extension Collection where Element == UInt8 { var data: Data { return Data(self) } }

let bytes: [UInt8] = [1, 2] let uint16 = bytes.data.uint16 print(uint16) // 513


La respuesta de Martin R es excelente y está bien actualizada para la versión beta 6. Sin embargo, si necesita obtener bytes que no estén al comienzo de su búfer, el método propuesto withMemoryRebound no ofrece un rango para volver a vincular. Mi solución a esto, por ejemplo. Elegir el segundo UInt16 de una matriz fue:

var val: UInt16 = 0 let buf = UnsafeMutableBufferPointer(start: &val, count: 1) _ = dat.copyBytes(to: buf, from: Range(2...3))


No conozco la sintaxis de swift, pero ¿qué pasa con algo como:

let a:UInt16 = UInt16(bytes[0]) * 256 + UInt16(bytes[1])


Qué tal si

let bytes:[UInt8] = [0x01, 0x02] let result = (UInt16(bytes[1]) << 8) + UInt16(bytes[0])

Con un bucle, esto se generaliza fácilmente a matrices de bytes más grandes, y se puede envolver en una función para facilitar la lectura:

let bytes:[UInt8] = [0x01, 0x02, 0x03, 0x04] func bytesToUInt(byteArray: [UInt8]) -> UInt { assert(byteArray.count <= 4) var result: UInt = 0 for idx in 0..<(byteArray.count) { let shiftAmount = UInt((byteArray.count) - idx - 1) * 8 result += UInt(byteArray[idx]) << shiftAmount } return result } println(bytesToUInt(bytes)) // result is 16909060


Si los bytes están en un objeto NSData , puede hacerlo (asumiendo data:NSData ):

var number: UInt16 = 0 data.getBytes(&number, length: sizeof(UInt16))

El método getBytes escribe hasta dos bytes en la ubicación de la memoria del number (similar a memcpy de memcpy Esto no bloqueará su aplicación si los data no tienen suficientes bytes).

( Edición: no es necesario utilizar el rango si se inicia desde el inicio del búfer)


Si quieres ir a través de NSData entonces funcionaría así:

let bytes:[UInt8] = [0x01, 0x02] println("bytes: /(bytes)") // bytes: [1, 2] let data = NSData(bytes: bytes, length: 2) print("data: /(data)") // data: <0102> var u16 : UInt16 = 0 ; data.getBytes(&u16) // Or: let u16 = UnsafePointer<UInt16>(data.bytes).memory println("u16: /(u16)") // u16: 513

Alternativamente:

let bytes:[UInt8] = [0x01, 0x02] let u16 = UnsafePointer<UInt16>(bytes).memory print("u16: /(u16)") // u16: 513

Ambas variantes asumen que los bytes están en el orden de bytes del host.

Actualización para Swift 3 (Xcode 8):

let bytes: [UInt8] = [0x01, 0x02] let u16 = UnsafePointer(bytes).withMemoryRebound(to: UInt16.self, capacity: 1) { $0.pointee } print("u16: /(u16)") // u16: 513