如何在Swift中将双精度数转换为字节数组?

20

我知道如何在Java中完成它(请参见这里),但我找不到Swift对应于Java的ByteBuffer,因此也就没有了.putDouble(double value)方法。

基本上,我正在寻找这样一个函数:

func doubleToByteArray(value: Double) -> [UInt8]? {
    . . .
}
doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]
7个回答

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

2
只需使用toByteArray(1729.1729).reverse()即可获得您要查找的顺序。 - David Gomez
1
我建议使用CFByteOrder.h中定义的函数,例如toByteArray(CFConvertDoubleHostToSwapped(1729.1729).v) - rintaro
我添加了 typealias Byte = UInt8,因为 Byte 不是本地数据类型。 :-) - Michael Dorner
如果您使用第一个函数将数据序列化到某些非Swift平台,请注意。如果您传递一个“可选”的Swift值类型,则它会向输出添加一个额外的字节,可能是为了指示该值是否为空。因此,给它一个UInt64?值会产生9个字节而不是8个字节。避免这种情况的一种方法是将“value”的参数类型从T更改为T?,然后用“var value = value!”替换第一个语句。 - RenniePet
它崩溃了:http://stackoverflow.com/questions/42252124/fatal-error-load-from-misaligned-raw-pointer - Vyacheslav
我认为 withUnsafeBytes(of: &y, Array.init) 是将 UnsafeMutableBufferPointer 转换为 Array 的更有效的方法。因为直接传递了 Array 初始化器。 - Damiaan Dufaux

6

好的,虽然很不容易,但是这里就是它:

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
}

请谨慎使用。


4
这是我对原解决方案的更新版本。
/// 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))
        }
    }
}

提示:不要忘记将 Byte 替换为 UInt8。


4

由于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


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

1

上述方法适用于使用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

我已经在playground中使用Xcode 7.2进行了测试。


1
我想在Swift 3 beta 6中使用它,以及新的withMemoryRebound命令。有人知道如何将其转换为Swift 3 beta 6吗? - j.s.com

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接