【问题标题】:Split UInt32 into [UInt8] in swift快速将 UInt32 拆分为 [UInt8]
【发布时间】:2015-04-30 14:17:22
【问题描述】:

我想将UInt32 添加到我使用[UInt8] 的字节缓冲区。在 java 中,有一个方便的 ByteBuffer 类,它具有类似 putInt() 的方法,用于完全类似的情况。这怎么能快速完成?

我想我可以通过以下方式解决这个问题:

let example: UInt32 = 72 << 24 | 66 << 16 | 1 << 8 | 15
var byteArray = [UInt8](count: 4, repeatedValue: 0)

for i in 0...3 {
    byteArray[i] = UInt8(0x0000FF & example >> UInt32((3 - i) * 8))
}

这很冗长,还有更简单的方法吗?

【问题讨论】:

    标签: swift bitwise-operators


    【解决方案1】:

    你的循环可以更简洁地写成

    let byteArray = 24.stride(through: 0, by: -8).map {
        UInt8(truncatingBitPattern: example >> UInt32($0))
    }
    

    或者,创建一个 UnsafeBufferPointer 并将其转换为 到一个数组:

    let example: UInt32 = 72 << 24 | 66 << 16 | 1 << 8 | 15
    
    var bigEndian = example.bigEndian
    let bytePtr = withUnsafePointer(&bigEndian) {
        UnsafeBufferPointer<UInt8>(start: UnsafePointer($0), count: sizeofValue(bigEndian))
    }
    let byteArray = Array(bytePtr)
    
    print(byteArray) // [72, 66, 1, 15]
    

    Swift 3(Xcode 8 beta 6)更新:

    var bigEndian = example.bigEndian
    let count = MemoryLayout<UInt32>.size
    let bytePtr = withUnsafePointer(to: &bigEndian) {
        $0.withMemoryRebound(to: UInt8.self, capacity: count) {
            UnsafeBufferPointer(start: $0, count: count)
        }
    }
    let byteArray = Array(bytePtr)
    

    【讨论】:

    • 谢谢!乍一看,这个循环似乎更难阅读,但它确实更紧凑。
    【解决方案2】:

    改进了@Martin R 的回答。在 UInt16、UInt32 和 UInt64 上工作:

    protocol UIntToBytesConvertable {
        var toBytes: [Byte] { get }
    }
    
    extension UIntToBytesConvertable {
        func toByteArr<T: Integer>(endian: T, count: Int) -> [Byte] {
            var _endian = endian
            let bytePtr = withUnsafePointer(to: &_endian) {
                $0.withMemoryRebound(to: Byte.self, capacity: count) {
                    UnsafeBufferPointer(start: $0, count: count)
                }
            }
            return [Byte](bytePtr)
        }
    }
    
    extension UInt16: UIntToBytesConvertable {
        var toBytes: [Byte] {
            return toByteArr(endian: self.littleEndian,
                             count: MemoryLayout<UInt16>.size)
        }
    }
    
    extension UInt32: UIntToBytesConvertable {
        var toBytes: [Byte] {
            return toByteArr(endian: self.littleEndian,
                             count: MemoryLayout<UInt32>.size)
        }
    }
    
    extension UInt64: UIntToBytesConvertable {
        var toBytes: [Byte] {
            return toByteArr(endian: self.littleEndian,
                             count: MemoryLayout<UInt64>.size)
        }
    }
    

    【讨论】:

      【解决方案3】:

      我也有类似的需求,正在尝试学习一些有关手动内存管理的知识。我是否遗漏了什么,或者使用新版本的 Swift 变得更容易了? (我使用的是 Swift 5)

      UInt32 创建一个字节数组 ([UInt8])

      let example: UInt32 = 1
      
      let byteArray = withUnsafeBytes(of: example.bigEndian) {
          Array($0)
      }
      
      print(byteArray) // [0, 0, 0, 1]
      

      或者如果你想追加到现有数组,可以在闭包中完成:

      var existingArray: [UInt8] = [1, 2, 3]
      let example: UInt32 = 1
      
      withUnsafeBytes(of: example.bigEndian) {
          existingArray.append(contentsOf: $0)
      }
      
      print(existingArray)  // [1, 2, 3, 0, 0, 0, 1]
      

      【讨论】:

        【解决方案4】:

        您可以从一种 UnsafeMutablePointer 类型转换为另一种:

        var arr = UnsafeMutablePointer<UInt32>.alloc(1)
        arr.memory = example
        var arr2 = UnsafeMutablePointer<UInt8>(arr)
        

        【讨论】:

          【解决方案5】:

          改进了@Martin R 的回答。

          func toByteArrary<T>(value: T)  -> [UInt8] where T: UnsignedInteger, T: FixedWidthInteger{
            var bigEndian = value.bigEndian
            let count = MemoryLayout<T>.size
            let bytePtr = withUnsafePointer(to: &bigEndian) {
              $0.withMemoryRebound(to: UInt8.self, capacity: count) {
                  UnsafeBufferPointer(start: $0, count: count)
              }
            }
          
            return Array(bytePtr)
          }
          

          【讨论】:

            【解决方案6】:

            另一种选择是直接扩展FixedWidthInteger协议,这样任何UnsignedInteger都会自动免费获得该功能。这就是它在@Benson 的回答上的样子:

            extension FixedWidthInteger where Self: UnsignedInteger {
            
                var bytes: [UInt8] {
                    var _endian = littleEndian
                    let bytePtr = withUnsafePointer(to: &_endian) {
                        $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) {
                            UnsafeBufferPointer(start: $0, count: MemoryLayout<Self>.size)
                        }
                    }
                    return [UInt8](bytePtr)
                }
            
            }
            

            我们可以通过单元测试来验证这一点:

            func test_bytes() {
                XCTAssertEqual(UInt8.min.bytes, [0])
                XCTAssertEqual(UInt8.max.bytes, [255])
            
                XCTAssertEqual(UInt16.min.bytes, [0, 0])
                XCTAssertEqual(UInt16.max.bytes, [255, 255])
            
                XCTAssertEqual(UInt32.min.bytes, [0, 0, 0, 0])
                XCTAssertEqual(UInt32.max.bytes, [255, 255, 255, 255])
            
                XCTAssertEqual(UInt64.min.bytes, [0, 0, 0, 0, 0, 0, 0, 0])
                XCTAssertEqual(UInt64.max.bytes, [255, 255, 255, 255, 255, 255, 255, 255])
            }
            

            【讨论】:

              【解决方案7】:

              我对 Xcode 8 Beta 6 也有类似的问题: 写下这一行

              var value = String(cString: sqlite3_column_text(stmt, index))
              
                 to
              
              
              let txt = UnsafePointer<Int8>(sqlite3_column_text(stmt, index))
              

              解决问题

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-04-04
                • 1970-01-01
                • 2011-09-23
                • 2017-03-11
                • 2016-03-12
                • 1970-01-01
                • 1970-01-01
                • 2017-02-20
                相关资源
                最近更新 更多