Подтвердить что ты не робот

Конвертировать байты/массив UInt8 в Int в Swift

Как преобразовать 4-байтовый массив в соответствующий Int?

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

Пример:

Входной сигнал:

\0\0\0\x0e

Вывод:

14

Некоторый код, который я нашел в Интернете, который не работает:

let data = NSData(bytes: array, length: 4)
data.getBytes(&size, length: 4)
// the output to size is 184549376
4b9b3361

Ответ 1

Есть две проблемы:

  • Int - это 64-разрядное целое на 64-разрядных платформах, ваши входные данные имеет только 32-разрядную версию.
  • Int использует малоинтенсивное представление на всех текущих платформах Swift, ваш вход большой.

При этом будет работать следующее:

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

Или используя Data в Swift 3:

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

С помощью некоторого волшебства указателя буфера вы можете избежать промежуточного скопируйте объект NSData (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

Для версии этого подхода Swift 3 см. ответ при освещении.

Ответ 2

В Swift 3 теперь немного более многословно:

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)

Ответ 3

Я думаю, что ответ Мартина лучше этого, но я все еще хочу опубликовать свой. Любое предложение было бы действительно полезно.

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

Ответ 4

Здесь есть несколько хороших ответов, которые действительно приятно видеть ^^ Однако, если вы хотите избежать взаимодействия с C-interopability API в Swift, то я рекомендую взглянуть на мой пример. Он также универсален для всех размеров типов данных. Обратите внимание, что MemoryLayout используется только для проверки работоспособности.

Код:

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)
    }
}

Пример использования:

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

Для поддержки for byte in bytes.reversed() вместо for byte in bytes.reversed() вам нужно for byte in bytes.reversed().

Ответ 5

Проблема с принятым ответом возникает, когда вы не знаете размер своего байтового массива (или размер Data)

Хорошо работает с let array: [UInt8] = [0, 0, 0x23, 0xFF]

Но он не будет работать с let array: [UInt8] = [0x23, 0xFF]
(потому что он будет рассматриваться как [0x23, 0xFF, 0, 0])

Вот почему мне нравится @Jerry, с побитовой операцией.

Я сделал функциональную версию его фрагмента кода.

let data = Data(bytes: [0x23, 0xFF])
let decimalValue = data.reduce(0) { v, byte in
    return v << 8 | Int(byte)
}

Ответ 6

Обновлено для Swift 5, две вещи, на которые следует обратить внимание:

  • Поскольку [UInt8] хранится в смежной области памяти, нет необходимости преобразовывать его в Data, указатель может напрямую обращаться ко всем байтам.

  • Порядок байтов Int в настоящее время почти не имеет порядка на всех платформах Apple, но на других платформах это не гарантируется.

скажем, мы хотим [0, 0, 0, 0x0e] преобразовать в 14. (порядок байтов с прямым порядком байтов)

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

Ответ 7

Для тех, кто предпочитает делать это старомодным способом, вот набор методов для получения значений int из байтового массива. Это предназначено для ситуаций, когда массив байтов, содержащий различные типы данных, обрабатывается последовательно.

/// 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
   }
}

Это выдержка из более крупного класса, которая включает в себя методы для извлечения строк и других видов данных. См. Также здесь: fooobar.com/info/264189/...