【问题标题】:Convert bytes/UInt8 array to Int in Swift在 Swift 中将 bytes/UInt8 数组转换为 Int
【发布时间】:2015-09-24 20:01:44
【问题描述】:

如何将一个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

【问题讨论】:

  • @MartinR 输出错误,这是一个真正的大数字
  • @MartinR 感谢您回复我的其他帖子。但到目前为止,它们都没有真正解决。

标签: arrays swift int byte


【解决方案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

或者在 Swift 3 中使用 Data

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 版本,请参阅环境光的答案。

【讨论】:

  • @HansBrende: NSData 在 Swift 3 中仍然有一个 getBytes 方法,只有它的覆盖类型 Data 没有。我添加了一个Data 版本,Swift 3 的另一个解决方案已经被环境光发布了。
  • @HansBrende:另请参阅stackoverflow.com/questions/38023838/…,了解更一般的数据转换处理方法。
【解决方案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)

【讨论】:

  • 这种性能表现如何?数据复制和分配/保留周期方面的任何开销?我想用 [UInt8] 替换我的 server-swift 代码中的 NSMutableData
【解决方案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

【讨论】:

  • 那为什么不接受马丁的回答呢? “upvotes”的数量下方有一个勾选按钮。它是一个勾号,当你点击它时,它会变成绿色。
  • 其实你的回答对我很有帮助。我用接受的方法得到了错误的 int 值,但你给了我正确的结果。我可能只是错误地执行了公认的方法,但仍然......
  • 签名号码失败。如何为有符号整数修改这个?例如:[0xFF,0xFF,0xFF,0xAB]
【解决方案4】:

这里有一些很好的答案,很高兴看到^^ 但是,如果您想避免与 Swift 的 C 互操作性 API 进行交互,那么我建议您看一下我的示例。对于所有数据类型大小,它也一样通用。请注意,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)

对于 little endian 支持,您需要 for byte in bytes.reversed()

说明:

0b00000001 &lt;&lt; 7 == 0b10000000

|= 是按位或赋值运算符: 它在左右手操作数上应用按位或,例如:

0b00000001 | 0b10000000 == 0b10000001

因此,当您有一个包含 2 个未分割字节的数组并希望将其转换为未分割字节时,您可以简单地;

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)

【讨论】:

  • 您能解释一下按位运算或链接到解释吗?
  • @aleclarson 当然,
【解决方案5】:

当您不知道字节数组的大小(或您的 Data 大小)时,问题就出现了

它适用于let array : [UInt8] = [0, 0, 0x23, 0xFF]

但它不适用于let array : [UInt8] = [0x23, 0xFF]
(因为它会被认为是[0x23, 0xFF, 0, 0]

这就是我喜欢@Jerry 的原因,它具有按位运算。

我已经制作了他的代码 sn-p 的功能版本。

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平台上都是little endian,但在其他平台上不保证。

假设我们希望 [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
       }
    }
    

    这是从一个更大的类中提取的,其中包括用于提取字符串和其他类型数据的方法。也可以在这里查看:https://stackoverflow.com/a/41592206/253938

    【讨论】:

      猜你喜欢
      • 2012-01-08
      • 2016-07-24
      • 1970-01-01
      • 2023-04-05
      • 2016-06-22
      • 2020-03-03
      • 2017-04-01
      • 1970-01-01
      • 2014-09-06
      相关资源
      最近更新 更多