Convert bytes/UInt8 array to Int in Swift

前端 未结 7 549
野性不改
野性不改 2020-12-01 03:56

How to convert a 4-bytes array into the corresponding Int?

let array: [UInt8] ==> let value : Int

Example:

Input:

         


        
相关标签:
7条回答
  • 2020-12-01 04:12

    In Swift 3 it is now a bit more wordy:

    let array : [UInt8] = [0, 0, 0, 0x0E]
    let bigEndianValue = array.withUnsafeBufferPointer {
             ($0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 1) { $0 })
    }.pointee
    let value = UInt32(bigEndian: bigEndianValue)
    
    0 讨论(0)
  • 2020-12-01 04:15

    There are two problems:

    • Int is a 64-bit integer on 64-bit platforms, your input data has only 32-bit.
    • Int uses a little-endian representation on all current Swift platforms, your input is big-endian.

    That being said the following would work:

    let array : [UInt8] = [0, 0, 0, 0x0E]
    var value : UInt32 = 0
    let data = NSData(bytes: array, length: 4)
    data.getBytes(&value, length: 4)
    value = UInt32(bigEndian: value)
    
    print(value) // 14
    

    Or using Data in Swift 3:

    let array : [UInt8] = [0, 0, 0, 0x0E]
    let data = Data(bytes: array)
    let value = UInt32(bigEndian: data.withUnsafeBytes { $0.pointee })
    

    With some buffer pointer magic you can avoid the intermediate copy to an NSData object (Swift 2):

    let array : [UInt8] = [0, 0, 0, 0x0E]
    var value = array.withUnsafeBufferPointer({ 
         UnsafePointer<UInt32>($0.baseAddress).memory
    })
    value = UInt32(bigEndian: value)
    
    print(value) // 14
    

    For a Swift 3 version of this approach, see ambientlight's answer.

    0 讨论(0)
  • 2020-12-01 04:18

    For those who prefer to do it the old-fashioned way, here's a set of methods for getting int values from a byte array. This is intended for situations where a byte array containing various kinds of data is being processed sequentially.

    /// Class which encapsulates a Swift byte array (an Array object with elements of type UInt8) and an
    /// index into the array.
    open class ByteArrayAndIndex {
    
       private var _byteArray : [UInt8]
       private var _arrayIndex = 0
    
       public init(_ byteArray : [UInt8]) {
          _byteArray = byteArray;
       }
    
       /// Property to provide read-only access to the current array index value.
       public var arrayIndex : Int {
          get { return _arrayIndex }
       }
    
       /// Property to calculate how many bytes are left in the byte array, i.e., from the index point
       /// to the end of the byte array.
       public var bytesLeft : Int {
          get { return _byteArray.count - _arrayIndex }
       }
    
       /// Method to get a single byte from the byte array.
       public func getUInt8() -> UInt8 {
          let returnValue = _byteArray[_arrayIndex]
          _arrayIndex += 1
          return returnValue
       }
    
       /// Method to get an Int16 from two bytes in the byte array (little-endian).
       public func getInt16() -> Int16 {
          return Int16(bitPattern: getUInt16())
       }
    
       /// Method to get a UInt16 from two bytes in the byte array (little-endian).
       public func getUInt16() -> UInt16 {
          let returnValue = UInt16(_byteArray[_arrayIndex]) |
                            UInt16(_byteArray[_arrayIndex + 1]) << 8
          _arrayIndex += 2
          return returnValue
       }
    
       /// Method to get a UInt from three bytes in the byte array (little-endian).
       public func getUInt24() -> UInt {
          let returnValue = UInt(_byteArray[_arrayIndex]) |
                            UInt(_byteArray[_arrayIndex + 1]) << 8 |
                            UInt(_byteArray[_arrayIndex + 2]) << 16
          _arrayIndex += 3
          return returnValue
       }
    
       /// Method to get an Int32 from four bytes in the byte array (little-endian).
       public func getInt32() -> Int32 {
          return Int32(bitPattern: getUInt32())
       }
    
       /// Method to get a UInt32 from four bytes in the byte array (little-endian).
       public func getUInt32() -> UInt32 {
          let returnValue = UInt32(_byteArray[_arrayIndex]) |
                            UInt32(_byteArray[_arrayIndex + 1]) << 8 |
                            UInt32(_byteArray[_arrayIndex + 2]) << 16 |
                            UInt32(_byteArray[_arrayIndex + 3]) << 24
          _arrayIndex += 4
          return returnValue
       }
    
       /// Method to get an Int64 from eight bytes in the byte array (little-endian).
       public func getInt64() -> Int64 {
          return Int64(bitPattern: getUInt64())
       }
    
       /// Method to get a UInt64 from eight bytes in the byte array (little-endian).
       public func getUInt64() -> UInt64 {
          let returnValue = UInt64(_byteArray[_arrayIndex]) |
                            UInt64(_byteArray[_arrayIndex + 1]) << 8 |
                            UInt64(_byteArray[_arrayIndex + 2]) << 16 |
                            UInt64(_byteArray[_arrayIndex + 3]) << 24 |
                            UInt64(_byteArray[_arrayIndex + 4]) << 32 |
                            UInt64(_byteArray[_arrayIndex + 5]) << 40 |
                            UInt64(_byteArray[_arrayIndex + 6]) << 48 |
                            UInt64(_byteArray[_arrayIndex + 7]) << 56
          _arrayIndex += 8
          return returnValue
       }
    }
    

    This is an extract from a larger class that includes methods for extracting strings and other kinds of data. See also here: https://stackoverflow.com/a/41592206/253938

    0 讨论(0)
  • 2020-12-01 04:21

    Updated for Swift 5, two things to pay attention:

    • As [UInt8] is stored in a contiguous region of memory, there's no need to convert it to Data, pointer can access all bytes directly.

    • Int's byte order is little endian currently on all Apple platform, but this is not garanteed on other platforms.

    say we want [0, 0, 0, 0x0e] to convert to 14. (big-endian byte order)

    let source: [UInt8] = [0, 0, 0, 0x0e]
    let bigEndianUInt32 = source.withUnsafeBytes { $0.load(as: UInt32.self) }
    let value = CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderLittleEndian.rawValue)
        ? UInt32(bigEndian: bigEndianUInt32)
        : bigEndianUInt32
    print(value) // 14
    
    0 讨论(0)
  • 2020-12-01 04:23

    There's some good answers here, which is really nice to see ^^ However if you'd like to avoid interacting with the C-interopability API of Swift, then I recommend to take a look at my example. It's also just as generic for all the datatype sizes. Note that MemoryLayout is only being used a sanity check.

    Code:

    public extension UnsignedInteger {
        init(_ bytes: [UInt8]) {
            precondition(bytes.count <= MemoryLayout<Self>.size)
    
            var value: UInt64 = 0
    
            for byte in bytes {
                value <<= 8
                value |= UInt64(byte)
            }
    
            self.init(value)
        }
    }
    

    Example usage:

    let someBytes = [UInt8](repeating: 0x42, count: 2)
    let someValue = UInt16(someBytes)
    

    For little endian support, you need for byte in bytes.reversed() instead.

    Explanation:

    <<= is the bitwise left shift assignment operator: It shifts the left hand operand (usually a numerical value type) by N bits defined by the right hand operand, for example:

    0b00000001 << 7 == 0b10000000

    |= is the bitwise or assignment operator: It applies a bitwise or on the left and right hand operands, for example:

    0b00000001 | 0b10000000 == 0b10000001

    So when you have an array of 2 unsinged bytes and want to convert it a unsinged short you can simply;

    let bytes = [UInt8](repeating: UInt8(255), count: 2)
    var short: UInt16 = 0
    
    // "add" our first unsinged byte
    short |= UInt16(bytes[0])
    // our short now looks like this in memory: 0b0000000011111111
    
    // make room for the unsinged byte ;)
    short <<= 8 
    // our short now looks like this in memory: 0b1111111100000000
    
    // "add" our last unsinged byte
    short |= UInt16(bytes[1])
    // our short now looks like this in memory: 0b1111111111111111
    
    print(short == UInt16.max)
    
    0 讨论(0)
  • 2020-12-01 04:27

    I think Martin's answer is better than this, but I still want to post mine. Any suggestion would be really helpful.

    let array : [UInt8] = [0, 0, 0, 0x0E]
    var value : Int = 0
    for byte in array {
        value = value << 8
        value = value | Int(byte)
    }
    print(value) // 14
    
    0 讨论(0)
提交回复
热议问题