【问题标题】:How to convert a double into a byte array in swift?如何快速将双精度数转换为字节数组?
【发布时间】:2015-01-13 05:42:02
【问题描述】:

我知道如何在 java 中执行此操作(请参阅here),但我找不到 java 的 ByteBuffer 的快速等效项,因此找不到它的 .putDouble(double value) 方法。

基本上,我正在寻找这样的功能:
func doubleToByteArray(value: Double) -> [UInt8]? {
    . . .
}
doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]

【问题讨论】:

    标签: ios swift byte bytearray bytebuffer


    【解决方案1】:
    typealias Byte = UInt8
    
    func toByteArray<T>(var value: T) -> [Byte] {
        return withUnsafePointer(&value) {
            Array(UnsafeBufferPointer(start: UnsafePointer<Byte>($0), count: sizeof(T)))
        }
    }
    
    toByteArray(1729.1729)
    toByteArray(1729.1729 as Float)
    toByteArray(1729)
    toByteArray(-1729)
    

    但结果与您的预期相反(因为字节序):

    [234, 149, 178, 12, 177, 4, 155, 64]
    [136, 37, 216, 68]
    [193, 6, 0, 0, 0, 0, 0, 0]
    [63, 249, 255, 255, 255, 255, 255, 255]
    

    添加:

    func fromByteArray<T>(value: [Byte], _: T.Type) -> T {
        return value.withUnsafeBufferPointer {
            return UnsafePointer<T>($0.baseAddress).memory
        }
    }
    
    let a: Double = 1729.1729
    let b = toByteArray(a) // -> [234, 149, 178, 12, 177, 4, 155, 64]
    let c = fromByteArray(b, Double.self) // -> 1729.1729
    

    对于 Xcode8/Swift3.0:

    func toByteArray<T>(_ value: T) -> [UInt8] {
        var value = value
        return withUnsafePointer(to: &value) {
            $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<T>.size) {
                Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<T>.size))
            }
        }
    }
    
    func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
        return value.withUnsafeBufferPointer {
            $0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) {
                $0.pointee
            }
        }
    }
    

    对于 Xcode8.1/Swift3.0.1

    func toByteArray<T>(_ value: T) -> [UInt8] {
        var value = value
        return withUnsafeBytes(of: &value) { Array($0) }
    }
    
    func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
        return value.withUnsafeBytes {
            $0.baseAddress!.load(as: T.self)
        }
    }
    

    【讨论】:

    • 只需使用toByteArray(1729.1729).reverse() 即可获得您要查找的订单。
    • 我推荐使用CFByteOrder.h中定义的函数,例如toByteArray(CFConvertDoubleHostToSwapped(1729.1729).v)
    • 我添加了typealias Byte = UInt8,因为Byte 不是本机数据类型。 :-)
    • 如果您使用第一个函数序列化数据到某些非 Swift 平台,请注意。如果你向它传递一个“可选”的 Swift 值类型,那么它会在输出中添加一个额外的字节,大概是为了指示该值是否为 nil。所以给它一个UInt64? value 产生 9 个字节而不是 8 个字节。避免这种情况的一种方法是将“value”的参数类型从 T 更改为 T?,然后将第一个语句替换为“var value = value!”。
    【解决方案2】:

    嗯,这并不容易,但就是这样:

    func doubleToByteArray(value: Double) -> [UInt8] {
        let count = sizeof(Double)
        var doubles: [Double] = [value]
        let data = NSData(bytes: doubles, length: count)
        var result = [UInt8](count: count, repeatedValue: 0)
        data.getBytes(&result, length: count)
        return result
    }
    

    谨慎使用。

    【讨论】:

      【解决方案3】:

      这是我对原始解决方案的更新版本。

      /// input: array of bytes 
      /// -> get pointer to byte array (UnsafeBufferPointer<[Byte]>)
      /// -> access its base address
      /// -> rebind memory to target type T (UnsafeMutablePointer<T>)
      /// -> extract and return the value of target type
      func binarytotype <T> (_ value: [Byte], _: T.Type) -> T
      {
          return value.withUnsafeBufferPointer {
              $0.baseAddress!
                .withMemoryRebound(to: T.self, capacity: 1) {
                  $0.pointee
              }
          }
      }
      
      /// input type: value of type T
      /// -> get pointer to value of T
      /// -> rebind memory to the target type, which is a byte array
      /// -> create array with a buffer pointer initialized with the     source pointer
      /// -> return the resulted array
      func typetobinary <T> (_ value: T) -> [Byte]
      {
          var mv : T = value
          let s : Int = MemoryLayout<T>.size
          return withUnsafePointer(to: &mv) {
              $0.withMemoryRebound(to: Byte.self, capacity: s) {
                  Array(UnsafeBufferPointer(start: $0, count: s))
              }
          }
      }
      

      PS:别忘了把 Byte 换成 UInt8。

      【讨论】:

        【解决方案4】:

        swift 3 中的解决方案:

        public func toByteArray<T>(_ value: T) -> [Byte] {
          let totalBytes = MemoryLayout<T>.size
          var value = value
          return withUnsafePointer(to: &value) { valuePtr in
            return valuePtr.withMemoryRebound(to: Byte.self, capacity: totalBytes) { reboundPtr in
              return Array(UnsafeBufferPointer(start: reboundPtr, count: totalBytes))
            }
          }
        }
        

        【讨论】:

          【解决方案5】:

          接受的答案很危险,因为MemoryLayout 为您提供了静态类型T 的大小!

          要解决此问题,您应该创建一个自定义协议并在其中请求Self

          protocol ByteConvertible {}
          
          extension ByteConvertible {
          
              func toBytes() -> [UInt8] {
          
                  let capacity = MemoryLayout<Self>.size
                  var mutableValue = self
                  return withUnsafePointer(to: &mutableValue) {
          
                      return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {
          
                          return Array(UnsafeBufferPointer(start: $0, count: capacity))
                      }
                  }
              }
          }
          

          我之前提到过,接受的答案很危险,下面是一个例子:

          let num = UInt8(42)
          MemoryLayout.size(ofValue: num) //=> 1 byte as expected
          let any: Any = num
          MemoryLayout.size(ofValue: any) //=> 32 bytes which is what will happen in the generic functions from the all the answers 
          

          Swift 3.0

          【讨论】:

            【解决方案6】:

            上述方法有效,使用 Swift 2,但我发现了一种更简单、更快速的方法来进行这种转换,反之亦然:

            func binarytotype <T> (value: [UInt8], _: T.Type) -> T
            {
                return value.withUnsafeBufferPointer
                {
                    return UnsafePointer<T>($0.baseAddress).memory
                }
            }
            
            func typetobinary <T> (var value: T) -> [UInt8]
            {
                return withUnsafePointer(&value)
                {
                    Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
                }
            }
            
            let a: Double = 0.25
            let b: [UInt8] = typetobinary(a) // -> [0, 0, 0, 0, 0, 0, 208, 63]
            let c = binarytotype(b, Double.self) // -> 0.25
            

            我已经在操场上使用 Xcode 7.2 对其进行了测试。

            【讨论】:

            • 我想在 Swift 3 beta 6 和新的 withMemoryRebound 命令中使用它。有人知道如何将其转换为 swift 3 beta 6 吗?
            【解决方案7】:
            func byteArray<T>(_ value: T) -> [UInt8] {
                var value = value
                var initialArray = withUnsafeBytes(of: &value) { Array($0) }
            
                initialArray.reverse()
                var count = initialArray.count
                while initialArray.first == 0 && count > 1 {
                    initialArray[0...count - 2] = initialArray[1...count - 1]
                    count -= 1
                }
                if initialArray[0] >= 128 {
                    var newArray = [UInt8](repeating: 0, count: count + 1)
                    newArray[0] = UInt8(0)
                    newArray[1...count] = initialArray[0...count - 1]
                    return newArray
                } else {
                    return Array(initialArray[0...count - 1])
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2016-07-23
              • 1970-01-01
              • 2013-03-10
              • 1970-01-01
              • 2014-11-02
              • 2010-11-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多