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