简体   繁体   中英

Convert a two byte UInt8 array to a UInt16 in Swift

With Swift I want to convert bytes from a uint8_t array to an integer.

"C" Example:

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

Swift Attempt:

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

What is the correct syntax or code to create a UInt16 value from a UInt8 array in Swift?

I am interested in the NSData version and am looking for a solution that does not use a temp array.

If you want to go via NSData then it would work like this:

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

Alternatively:

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

Both variants assume that the bytes are in the host byte order.

Update for 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

In Swift 5 or later you can convert the bytes [UInt8] to UInt16 value using withUnsafeBytes { $0.load(as: UInt16.self) }

let bytes: [UInt8] = [1, 2]

loading as UInt16

let uint16 = bytes.withUnsafeBytes { $0.load(as: UInt16.self) }    // 513 

To get rid of the verbosity we can create a generic method extending ContiguousBytes :

extension ContiguousBytes {
    func object<T>() -> T {
        withUnsafeBytes { $0.load(as: T.self) }
    }
}

Usage:

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

And to access the bytes anywhere in the collection:

extension Data {
    func subdata<R: RangeExpression>(in range: R) -> Self where R.Bound == Index {
        subdata(in: range.relative(to: self) )
    }
    func object<T>(at offset: Int) -> T {
        subdata(in: offset...).object()
    }
}

extension Sequence where Element == UInt8  {
    var data: Data { .init(self) }
}

extension Collection where Element == UInt8, Index == Int {
    func object<T>(at offset: Int = 0) -> T {
        data.object(at: offset)
    }
}

Usage:

let bytes: [UInt8] = [255, 255, 1, 2]
let uintMax: UInt16 = bytes.object()      // 65535 at offset zero
let uint16: UInt16 = bytes.object(at: 2)  // 513   at offset two

How about

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

With a loop, this easily generalizes to larger byte arrays, and it can be wrapped in a function for readability:

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

我不知道 swift 的语法,但是像这样的东西呢:

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

If the bytes are in an NSData object you may do (assume data:NSData ):

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

The getBytes method writes up to two bytes in the memory location of number (similar to C's memcpy . This won't crash your app if data hasn't enough bytes.

( edit: no need to use range if starting from beginning of buffer)

Martin R's answer is great and nicely updated for beta 6. However, if you need to get at bytes that are not at the start of your buffer the proposed withMemoryRebound method does not offer a range to rebind from. My solution to this, eg. pick out the second UInt16 from an array was:

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

Assuming little endian encoding.

To convert to UInt16 from [UInt8], you can do something like

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

For conversion to UInt32, this pattern extends to

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

Octal representation of the shift amount gives a nice indication on how many full bytes are shifted (8 becomes 0o10, 16 becomes 0o20 etc).

This can be reduced to the following for UInt16:

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

and to the following for UInt32:

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

The reduced version also works for UInt64, and also handles values where the byte encoding does not use all bytes, like [0x01, 0x02, 0x03]

i putting here for helper. Very simple converter UINT16 to [UINT8].

var value: UInt16 = 2802

func uint16ToUint8Array(value: UInt16) -> [UInt8]{
    var uint8Array = [UInt8]()
    let firstByte = UInt8(value >> 8)
    uint8Array.append(firstByte)
    let secondBtye = UInt8(value.bigEndian >> 8)
    uint8Array.append(secondBtye)
    return uint8Array
}
  print(uint16ToUint8Array(value: value))

Output: [10, 242]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM